<!DOCTYPE html><html lang="zh-Hans"><head><meta charset="utf-8"><title>Angular - Angular CLI 构建器（Builder）</title><meta name="Description" content="Angular is a platform for building mobile and desktop web applications.
    Join the community of millions of developers who build compelling user interfaces with Angular."><base href="/"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="search" type="application/opensearchdescription+xml" href="assets/opensearch.xml"><link rel="icon" type="image/x-icon" href="assets/images/favicons/favicon.ico"><link rel="icon" type="image/png" href="assets/images/favicons/favicon-32x32.png" sizes="32x32"><link rel="icon" type="image/png" href="assets/images/favicons/favicon-194x194.png" sizes="194x194"><link rel="icon" type="image/png" href="assets/images/favicons/favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/png" href="assets/images/favicons/favicon-16x16.png" sizes="16x16"><link rel="apple-touch-icon" sizes="144x144" href="assets/images/favicons/favicon-144x144.png"><link rel="apple-touch-icon-precomposed" sizes="144x144" href="assets/images/favicons/favicon-144x144.png"><link href="assets/fonts/Material_Icons.css" rel="stylesheet"><link href="assets/fonts/Droid_Sans_Mono.css" rel="stylesheet"><link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"><link rel="manifest" href="pwa-manifest.json"><meta name="theme-color" content="#1976d2"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="translucent"><script async="" src="assets/js/analytics.js"></script><script>!function(e,a,n,t,s,c,g){e.GoogleAnalyticsObject=s,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,c=a.createElement(n),g=a.getElementsByTagName(n)[0],c.async=1,c.src="assets/js/analytics.js",~e.name.indexOf("NG_DEFER_BOOTSTRAP")||g.parentNode.insertBefore(c,g)}(window,document,"script",0,"ga")</script><script>window.onerror=function(){ga("send","exception",{exDescription:function(e,r,n,a,c){var l;e=e.replace(/^Error: /,""),l=c?c.stack.replace(/^Error: /,"").replace(e+"\n","").replace(/^ +/gm,"").replace(/^at /gm,"").replace(/(?: \(|@)http.+\/([^/)]+)\)?(?:\n|$)/gm,"@$1\n").replace(/ *\(eval code(:\d+:\d+)\)(?:\n|$)/gm,"@???$1\n"):r+":"+(n=n||"?")+":"+(a=a||"?");return(e+"\n"+l).substr(0,150)}.apply(null,arguments),exFatal:!0})}</script><link rel="stylesheet" href="styles.4adbe52ad34df01b5273.css"><style>.cdk-high-contrast-active .mat-toolbar{outline:solid 1px}.mat-toolbar-row,.mat-toolbar-single-row{display:flex;box-sizing:border-box;padding:0 16px;width:100%;flex-direction:row;align-items:center;white-space:nowrap}.mat-toolbar-multiple-rows{display:flex;box-sizing:border-box;flex-direction:column;width:100%}.mat-toolbar-multiple-rows{min-height:64px}.mat-toolbar-row,.mat-toolbar-single-row{height:64px}@media(max-width:599px){.mat-toolbar-multiple-rows{min-height:56px}.mat-toolbar-row,.mat-toolbar-single-row{height:56px}}</style><style>.mat-icon{background-repeat:no-repeat;display:inline-block;fill:currentColor;height:24px;width:24px}.mat-icon.mat-icon-inline{font-size:inherit;height:inherit;line-height:inherit;width:inherit}[dir=rtl] .mat-icon-rtl-mirror{transform:scale(-1,1)}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon{display:block}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button .mat-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button .mat-icon{margin:auto}</style><style>.mat-button .mat-button-focus-overlay,.mat-icon-button .mat-button-focus-overlay{opacity:0}.mat-button:hover .mat-button-focus-overlay,.mat-stroked-button:hover .mat-button-focus-overlay{opacity:.04}@media(hover:none){.mat-button:hover .mat-button-focus-overlay,.mat-stroked-button:hover .mat-button-focus-overlay{opacity:0}}.mat-button,.mat-flat-button,.mat-icon-button,.mat-stroked-button{box-sizing:border-box;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;outline:0;border:none;-webkit-tap-highlight-color:transparent;display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible}.mat-button::-moz-focus-inner,.mat-flat-button::-moz-focus-inner,.mat-icon-button::-moz-focus-inner,.mat-stroked-button::-moz-focus-inner{border:0}.mat-button[disabled],.mat-flat-button[disabled],.mat-icon-button[disabled],.mat-stroked-button[disabled]{cursor:default}.mat-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-button.cdk-program-focused .mat-button-focus-overlay,.mat-flat-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-flat-button.cdk-program-focused .mat-button-focus-overlay,.mat-icon-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-icon-button.cdk-program-focused .mat-button-focus-overlay,.mat-stroked-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-stroked-button.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-button::-moz-focus-inner,.mat-flat-button::-moz-focus-inner,.mat-icon-button::-moz-focus-inner,.mat-stroked-button::-moz-focus-inner{border:0}.mat-raised-button{box-sizing:border-box;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;outline:0;border:none;-webkit-tap-highlight-color:transparent;display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0,0,0);transition:background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1)}.mat-raised-button::-moz-focus-inner{border:0}.mat-raised-button[disabled]{cursor:default}.mat-raised-button.cdk-keyboard-focused .mat-button-focus-overlay,.mat-raised-button.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-raised-button::-moz-focus-inner{border:0}._mat-animation-noopable.mat-raised-button{transition:none;animation:none}.mat-stroked-button{border:1px solid currentColor;padding:0 15px;line-height:34px}.mat-stroked-button .mat-button-focus-overlay,.mat-stroked-button .mat-button-ripple.mat-ripple{top:-1px;left:-1px;right:-1px;bottom:-1px}.mat-fab{box-sizing:border-box;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;outline:0;border:none;-webkit-tap-highlight-color:transparent;display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0,0,0);transition:background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1);min-width:0;border-radius:50%;width:56px;height:56px;padding:0;flex-shrink:0}.mat-fab::-moz-focus-inner{border:0}.mat-fab[disabled]{cursor:default}.mat-fab.cdk-keyboard-focused .mat-button-focus-overlay,.mat-fab.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-fab::-moz-focus-inner{border:0}._mat-animation-noopable.mat-fab{transition:none;animation:none}.mat-fab .mat-button-wrapper{padding:16px 0;display:inline-block;line-height:24px}.mat-mini-fab{box-sizing:border-box;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;outline:0;border:none;-webkit-tap-highlight-color:transparent;display:inline-block;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;min-width:64px;line-height:36px;padding:0 16px;border-radius:4px;overflow:visible;transform:translate3d(0,0,0);transition:background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1);min-width:0;border-radius:50%;width:40px;height:40px;padding:0;flex-shrink:0}.mat-mini-fab::-moz-focus-inner{border:0}.mat-mini-fab[disabled]{cursor:default}.mat-mini-fab.cdk-keyboard-focused .mat-button-focus-overlay,.mat-mini-fab.cdk-program-focused .mat-button-focus-overlay{opacity:.12}.mat-mini-fab::-moz-focus-inner{border:0}._mat-animation-noopable.mat-mini-fab{transition:none;animation:none}.mat-mini-fab .mat-button-wrapper{padding:8px 0;display:inline-block;line-height:24px}.mat-icon-button{padding:0;min-width:0;width:40px;height:40px;flex-shrink:0;line-height:40px;border-radius:50%}.mat-icon-button .mat-icon,.mat-icon-button i{line-height:24px}.mat-button-focus-overlay,.mat-button-ripple.mat-ripple{top:0;left:0;right:0;bottom:0;position:absolute;pointer-events:none;border-radius:inherit}.mat-button-ripple.mat-ripple:not(:empty){transform:translateZ(0)}.mat-button-focus-overlay{opacity:0;transition:opacity .2s cubic-bezier(.35,0,.25,1),background-color .2s cubic-bezier(.35,0,.25,1)}._mat-animation-noopable .mat-button-focus-overlay{transition:none}.cdk-high-contrast-active .mat-button-focus-overlay{background-color:#fff}.cdk-high-contrast-black-on-white .mat-button-focus-overlay{background-color:#000}.mat-button-ripple-round{border-radius:50%;z-index:1}.mat-button .mat-button-wrapper>*,.mat-fab .mat-button-wrapper>*,.mat-flat-button .mat-button-wrapper>*,.mat-icon-button .mat-button-wrapper>*,.mat-mini-fab .mat-button-wrapper>*,.mat-raised-button .mat-button-wrapper>*,.mat-stroked-button .mat-button-wrapper>*{vertical-align:middle}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button{display:block;font-size:inherit;width:2.5em;height:2.5em}.cdk-high-contrast-active .mat-button,.cdk-high-contrast-active .mat-fab,.cdk-high-contrast-active .mat-flat-button,.cdk-high-contrast-active .mat-icon-button,.cdk-high-contrast-active .mat-mini-fab,.cdk-high-contrast-active .mat-raised-button{outline:solid 1px}</style><style>.mat-drawer-container{position:relative;z-index:1;box-sizing:border-box;-webkit-overflow-scrolling:touch;display:block;overflow:hidden}.mat-drawer-container[fullscreen]{top:0;left:0;right:0;bottom:0;position:absolute}.mat-drawer-container[fullscreen].mat-drawer-container-has-open{overflow:hidden}.mat-drawer-container.mat-drawer-container-explicit-backdrop .mat-drawer-side{z-index:3}.mat-drawer-container.ng-animate-disabled .mat-drawer-backdrop,.mat-drawer-container.ng-animate-disabled .mat-drawer-content,.ng-animate-disabled .mat-drawer-container .mat-drawer-backdrop,.ng-animate-disabled .mat-drawer-container .mat-drawer-content{transition:none}.mat-drawer-backdrop{top:0;left:0;right:0;bottom:0;position:absolute;display:block;z-index:3;visibility:hidden}.mat-drawer-backdrop.mat-drawer-shown{visibility:visible}.mat-drawer-transition .mat-drawer-backdrop{transition-duration:.4s;transition-timing-function:cubic-bezier(.25,.8,.25,1);transition-property:background-color,visibility}.cdk-high-contrast-active .mat-drawer-backdrop{opacity:.5}.mat-drawer-content{position:relative;z-index:1;display:block;height:100%;overflow:auto}.mat-drawer-transition .mat-drawer-content{transition-duration:.4s;transition-timing-function:cubic-bezier(.25,.8,.25,1);transition-property:transform,margin-left,margin-right}.mat-drawer{position:relative;z-index:4;display:block;position:absolute;top:0;bottom:0;z-index:3;outline:0;box-sizing:border-box;overflow-y:auto;transform:translate3d(-100%,0,0)}.cdk-high-contrast-active .mat-drawer,.cdk-high-contrast-active [dir=rtl] .mat-drawer.mat-drawer-end{border-right:solid 1px currentColor}.cdk-high-contrast-active .mat-drawer.mat-drawer-end,.cdk-high-contrast-active [dir=rtl] .mat-drawer{border-left:solid 1px currentColor;border-right:none}.mat-drawer.mat-drawer-side{z-index:2}.mat-drawer.mat-drawer-end{right:0;transform:translate3d(100%,0,0)}[dir=rtl] .mat-drawer{transform:translate3d(100%,0,0)}[dir=rtl] .mat-drawer.mat-drawer-end{left:0;right:auto;transform:translate3d(-100%,0,0)}.mat-drawer-inner-container{width:100%;height:100%;overflow:auto;-webkit-overflow-scrolling:touch}.mat-sidenav-fixed{position:fixed}</style><style>.nav-link.highlight[_ngcontent-ng-docs-c30]{color:#ff0}</style><script charset="utf-8" src="toc-toc-module-es2015.f8531f591ee147aebcc6.js"></script><script charset="utf-8" src="default~code-code-example-module~code-code-tabs-module-es2015.9efcc76d2979a2e605e0.js"></script><script charset="utf-8" src="code-code-example-module-es2015.fa05e6f700453813d256.js"></script><style>.mat-progress-bar{display:block;height:4px;overflow:hidden;position:relative;transition:opacity 250ms linear;width:100%}._mat-animation-noopable.mat-progress-bar{transition:none;animation:none}.mat-progress-bar .mat-progress-bar-element,.mat-progress-bar .mat-progress-bar-fill::after{height:100%;position:absolute;width:100%}.mat-progress-bar .mat-progress-bar-background{width:calc(100% + 10px)}.cdk-high-contrast-active .mat-progress-bar .mat-progress-bar-background{display:none}.mat-progress-bar .mat-progress-bar-buffer{transform-origin:top left;transition:transform 250ms ease}.cdk-high-contrast-active .mat-progress-bar .mat-progress-bar-buffer{border-top:solid 5px;opacity:.5}.mat-progress-bar .mat-progress-bar-secondary{display:none}.mat-progress-bar .mat-progress-bar-fill{animation:none;transform-origin:top left;transition:transform 250ms ease}.cdk-high-contrast-active .mat-progress-bar .mat-progress-bar-fill{border-top:solid 4px}.mat-progress-bar .mat-progress-bar-fill::after{animation:none;content:"";display:inline-block;left:0}.mat-progress-bar[dir=rtl],[dir=rtl] .mat-progress-bar{transform:rotateY(180deg)}.mat-progress-bar[mode=query]{transform:rotateZ(180deg)}.mat-progress-bar[mode=query][dir=rtl],[dir=rtl] .mat-progress-bar[mode=query]{transform:rotateZ(180deg) rotateY(180deg)}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-fill,.mat-progress-bar[mode=query] .mat-progress-bar-fill{transition:none}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-primary,.mat-progress-bar[mode=query] .mat-progress-bar-primary{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-primary-indeterminate-translate 2s infinite linear;left:-145.166611%}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-primary.mat-progress-bar-fill::after,.mat-progress-bar[mode=query] .mat-progress-bar-primary.mat-progress-bar-fill::after{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-primary-indeterminate-scale 2s infinite linear}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-secondary,.mat-progress-bar[mode=query] .mat-progress-bar-secondary{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-secondary-indeterminate-translate 2s infinite linear;left:-54.888891%;display:block}.mat-progress-bar[mode=indeterminate] .mat-progress-bar-secondary.mat-progress-bar-fill::after,.mat-progress-bar[mode=query] .mat-progress-bar-secondary.mat-progress-bar-fill::after{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-secondary-indeterminate-scale 2s infinite linear}.mat-progress-bar[mode=buffer] .mat-progress-bar-background{-webkit-backface-visibility:hidden;backface-visibility:hidden;animation:mat-progress-bar-background-scroll 250ms infinite linear;display:block}.mat-progress-bar._mat-animation-noopable .mat-progress-bar-background,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-buffer,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-fill,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-fill::after,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-primary,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-primary.mat-progress-bar-fill::after,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-secondary,.mat-progress-bar._mat-animation-noopable .mat-progress-bar-secondary.mat-progress-bar-fill::after{animation:none;transition:none}@keyframes mat-progress-bar-primary-indeterminate-translate{0%{transform:translateX(0)}20%{animation-timing-function:cubic-bezier(.5,0,.701732,.495819);transform:translateX(0)}59.15%{animation-timing-function:cubic-bezier(.302435,.381352,.55,.956352);transform:translateX(83.67142%)}100%{transform:translateX(200.611057%)}}@keyframes mat-progress-bar-primary-indeterminate-scale{0%{transform:scaleX(.08)}36.65%{animation-timing-function:cubic-bezier(.334731,.12482,.785844,1);transform:scaleX(.08)}69.15%{animation-timing-function:cubic-bezier(.06,.11,.6,1);transform:scaleX(.661479)}100%{transform:scaleX(.08)}}@keyframes mat-progress-bar-secondary-indeterminate-translate{0%{animation-timing-function:cubic-bezier(.15,0,.515058,.409685);transform:translateX(0)}25%{animation-timing-function:cubic-bezier(.31033,.284058,.8,.733712);transform:translateX(37.651913%)}48.35%{animation-timing-function:cubic-bezier(.4,.627035,.6,.902026);transform:translateX(84.386165%)}100%{transform:translateX(160.277782%)}}@keyframes mat-progress-bar-secondary-indeterminate-scale{0%{animation-timing-function:cubic-bezier(.15,0,.515058,.409685);transform:scaleX(.08)}19.15%{animation-timing-function:cubic-bezier(.31033,.284058,.8,.733712);transform:scaleX(.457104)}44.15%{animation-timing-function:cubic-bezier(.4,.627035,.6,.902026);transform:scaleX(.72796)}100%{transform:scaleX(.08)}}@keyframes mat-progress-bar-background-scroll{to{transform:translateX(-8px)}}</style><script charset="utf-8" src="assets-js-prettify-js-es2015.ba856648f2dd2d930a9b.js"></script></head><body><aio-shell ng-version="9.0.0-rc.11" class="aio-notification-hide folder-guide mode-stable page-guide-cli-builder sidenav-open view-SideNav"><div id="top-of-page"></div><mat-toolbar color="primary" class="mat-toolbar app-toolbar no-print mat-primary mat-toolbar-multiple-rows"><mat-toolbar-row class="mat-toolbar-row notification-container"><aio-notification notificationid="survey-february-2019" expirationdate="2019-03-01" class="ng-tns-c22-0 ng-trigger ng-trigger-hideAnimation" style="height:0"><span class="content ng-tns-c22-0"><a href="http://bit.ly/angular-survey-2019" target="_blank" class="ng-tns-c22-0"><mat-icon role="img" svgicon="insert_comment" aria-label="Announcement" class="mat-icon notranslate icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></mat-icon><span class="message"><b>填写这份《一分钟调查》</b>，帮我们（开发组）做得更好！</span><span class="action-button">去填写</span></a></span><button mat-icon-button="" aria-label="Close" class="close-button ng-tns-c22-0 mat-icon-button mat-button-base"><span class="mat-button-wrapper"><mat-icon role="img" svgicon="close" aria-label="Dismiss notification" class="mat-icon notranslate mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path><path d="M0 0h24v24H0z" fill="none"></path></svg></mat-icon></span><div matripple="" class="mat-ripple mat-button-ripple mat-button-ripple-round"></div><div class="mat-button-focus-overlay"></div></button></aio-notification></mat-toolbar-row><mat-toolbar-row class="mat-toolbar-row"><button mat-button="" title="Docs menu" class="hamburger mat-button mat-button-base"><span class="mat-button-wrapper"><mat-icon role="img" svgicon="menu" class="mat-icon notranslate mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></svg></mat-icon></span><div matripple="" class="mat-ripple mat-button-ripple"></div><div class="mat-button-focus-overlay"></div></button><a href="/" class="nav-link home"><img src="assets/images/logos/angular/logo-nav@2x.png" width="150" height="40" title="Home" alt="Home" class="ng-star-inserted"></a><aio-top-menu _nghost-ng-docs-c30="" class="ng-star-inserted"><ul _ngcontent-ng-docs-c30="" role="navigation"><li _ngcontent-ng-docs-c30="" class="ng-star-inserted"><a _ngcontent-ng-docs-c30="" class="nav-link" href="features" title="特性"><span _ngcontent-ng-docs-c30="" class="nav-link-inner">特性</span></a></li><li _ngcontent-ng-docs-c30="" class="ng-star-inserted"><a _ngcontent-ng-docs-c30="" class="nav-link" href="docs" title="文档"><span _ngcontent-ng-docs-c30="" class="nav-link-inner">文档</span></a></li><li _ngcontent-ng-docs-c30="" class="ng-star-inserted"><a _ngcontent-ng-docs-c30="" class="nav-link" href="resources" title="资源"><span _ngcontent-ng-docs-c30="" class="nav-link-inner">资源</span></a></li><li _ngcontent-ng-docs-c30="" class="ng-star-inserted"><a _ngcontent-ng-docs-c30="" class="nav-link" href="events" title="会议"><span _ngcontent-ng-docs-c30="" class="nav-link-inner">会议</span></a></li><li _ngcontent-ng-docs-c30="" class="ng-star-inserted"><a _ngcontent-ng-docs-c30="" class="nav-link" href="https://blog.angular.io/" title="博客"><span _ngcontent-ng-docs-c30="" class="nav-link-inner">博客</span></a></li><li _ngcontent-ng-docs-c30="" class="ng-star-inserted"><a _ngcontent-ng-docs-c30="" class="nav-link" href="translations/cn/home" title="关于中文版"><span _ngcontent-ng-docs-c30="" class="nav-link-inner">关于中文版</span></a></li></ul></aio-top-menu><aio-search-box class="search-container"><input type="search" aria-label="search" placeholder="搜索"></aio-search-box><div class="toolbar-external-icons-container"><a href="https://twitter.com/angular" title="Twitter" aria-label="Angular on twitter"><mat-icon role="img" svgicon="logos:twitter" class="mat-icon notranslate mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 50 59" xmlns="http://www.w3.org/2000/svg"><path d="M50,9.3c-1.8,0.8-3.8,1.4-5.9,1.6c2.1-1.3,3.7-3.3,4.5-5.7c-2,1.2-4.2,2-6.5,2.5c-1.9-2-4.5-3.2-7.5-3.2c-5.7,0-10.3,4.6-10.3,10.3c0,0.8,0.1,1.6,0.3,2.3C16.1,16.7,8.5,12.6,3.5,6.4c-0.9,1.5-1.4,3.3-1.4,5.2c0,3.6,1.8,6.7,4.6,8.5C5,20,3.4,19.6,2,18.8c0,0,0,0.1,0,0.1c0,5,3.5,9.1,8.2,10.1c-0.9,0.2-1.8,0.4-2.7,0.4c-0.7,0-1.3-0.1-1.9-0.2c1.3,4.1,5.1,7,9.6,7.1c-3.5,2.8-7.9,4.4-12.7,4.4c-0.8,0-1.6,0-2.4-0.1c4.5,2.9,9.9,4.6,15.7,4.6c18.9,0,29.2-15.6,29.2-29.2c0-0.4,0-0.9,0-1.3C46.9,13.2,48.6,11.4,50,9.3z"></path></svg></mat-icon></a><a href="https://github.com/angular/angular" title="GitHub" aria-label="Angular on github"><mat-icon role="img" svgicon="logos:github" class="mat-icon notranslate mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 51.8 50.4" xmlns="http://www.w3.org/2000/svg"><path d="M25.9,0.2C11.8,0.2,0.3,11.7,0.3,25.8c0,11.3,7.3,20.9,17.5,24.3c1.3,0.2,1.7-0.6,1.7-1.2c0-0.6,0-2.6,0-4.8c-7.1,1.5-8.6-3-8.6-3c-1.2-3-2.8-3.7-2.8-3.7c-2.3-1.6,0.2-1.6,0.2-1.6c2.6,0.2,3.9,2.6,3.9,2.6c2.3,3.9,6,2.8,7.5,2.1c0.2-1.7,0.9-2.8,1.6-3.4c-5.7-0.6-11.7-2.8-11.7-12.7c0-2.8,1-5.1,2.6-6.9c-0.3-0.7-1.1-3.3,0.3-6.8c0,0,2.1-0.7,7,2.6c2-0.6,4.2-0.9,6.4-0.9c2.2,0,4.4,0.3,6.4,0.9c4.9-3.3,7-2.6,7-2.6c1.4,3.5,0.5,6.1,0.3,6.8c1.6,1.8,2.6,4.1,2.6,6.9c0,9.8-6,12-11.7,12.6c0.9,0.8,1.7,2.4,1.7,4.7c0,3.4,0,6.2,0,7c0,0.7,0.5,1.5,1.8,1.2c10.2-3.4,17.5-13,17.5-24.3C51.5,11.7,40.1,0.2,25.9,0.2z"></path></svg></mat-icon></a></div></mat-toolbar-row></mat-toolbar><mat-sidenav-container role="main" class="mat-drawer-container mat-sidenav-container sidenav-container mat-drawer-transition has-floating-toc"><div class="mat-drawer-backdrop ng-star-inserted"></div><div class="cdk-visually-hidden cdk-focus-trap-anchor" aria-hidden="true"></div><mat-sidenav tabindex="-1" class="mat-drawer mat-sidenav sidenav ng-tns-c18-1 ng-trigger ng-trigger-transform mat-drawer-side ng-star-inserted mat-drawer-opened" style="transform:none;visibility:visible"><div class="mat-drawer-inner-container ng-tns-c18-1"><aio-nav-menu class="ng-tns-c18-1"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="docs" title="Angular 文档简介" target="_self"><span>简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-1 collapsed ng-star-inserted" title="通过构建第一个 Angular 应用来学习基础知识" aria-pressed="false"><span>快速上手</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="start" title="介绍 Angular 的组件模型、模板语法和组件通讯" target="_self"><span>你的第一个应用</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="start/routing" title="介绍如何使用浏览器的 URL 在组件之间进行路由" target="_self"><span>路由</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="start/data" title="介绍服务以及如何访问外部数据" target="_self"><span>管理数据</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="start/forms" title="学习如何使用表单从用户那里获取并管理数据" target="_self"><span>表单</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="start/deployment" title="通过你的应用放到 Firebase 或自己的服务器上来把它分享给外界" target="_self"><span>部署</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="guide/setup-local" title="使用 Angular CLI 搭建本地开发环境简介" target="_self"><span>搭建环境</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-1 collapsed ng-star-inserted" title="Angular 的基本原理" aria-pressed="false"><span>基本原理</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="介绍 Angular 应用中的一些基本概念。" aria-pressed="false"><span>Angular 的基本概念</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture" title="Angular 应用中的基本构造块。" target="_self"><span>基本概念简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture-modules" title="关于 Angular 模块（NgModules）。" target="_self"><span>模块简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture-components" title="关于组件、模板和视图。" target="_self"><span>组件简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture-services" title="关于服务与依赖注入。" target="_self"><span>服务与 DI 简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/architecture-next-steps" title="超越基础阶段。" target="_self"><span>后续步骤</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="英雄指南是这里大量 Angular 范例的基础" aria-pressed="false"><span>英雄指南</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="tutorial" title="《英雄指南》教程简介" target="_self"><span>简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt0" title="创建应用的外壳" target="_self"><span>应用的“外壳”</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt1" title="第一部分：构建一个简单的英雄编辑器" target="_self"><span>1. 英雄编辑器</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt2" title="第二部分：构建一个主从结构的页面，用于展现英雄列表。" target="_self"><span>2. 显示英雄列表</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt3" title="第三部分：把主从结构的视图重构成几个独立的组件。" target="_self"><span>3. 创建特性组件</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt4" title="第四部分：创建一个可复用的服务来管理英雄数据。" target="_self"><span>4. 添加服务</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt5" title="第五部分：添加 Angular 路由器，并且学习在视图之间导航。" target="_self"><span>5. 添加应用内导航</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="tutorial/toh-pt6" title="第六部分：通过 HTTP 来获取并保存英雄数据。" target="_self"><span>6. 从服务器获取数据</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="通过数据绑定构建动态视图" aria-pressed="false"><span>组件与模板</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/displaying-data" title="属性绑定可以帮助应用把数据显示在界面上" target="_self"><span>显示数据</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/template-syntax" title="学习如何写模板，以便借助数据绑定机制显示数据并响应事件。" target="_self"><span>模板语法</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/user-input" title="用户输入会触发 DOM 事件。Angular 会通过事件绑定来监听那些事件，并把修改后的值传回应用的组件和模型中。" target="_self"><span>用户输入</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/lifecycle-hooks" title="Angular 调用指令和组件的生命周期钩子函数，包括它的创建、变更和销毁时。" target="_self"><span>生命周期钩子</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/component-interaction" title="在不同的指令和组件之间共享信息" target="_self"><span>组件交互</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/component-styles" title="添加专属于某个组件的样式" target="_self"><span>组件样式</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dynamic-component-loader" title="动态加载组件" target="_self"><span>动态组件</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/elements" title="把组件转换成自定义元素" target="_self"><span>Angular 元素</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/attribute-directives" title="属性型指令把行为添加到现有元素上。" target="_self"><span>属性型指令</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/structural-directives" title="结构型指令可以操纵页面的布局" target="_self"><span>结构型指令</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/pipes" title="管道可以在模板中转换显示的内容。" target="_self"><span>管道</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="Angular 的表单" aria-pressed="false"><span>表单</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/forms-overview" title="表单可以创建集中、高效、引人注目的输入体验。Angular 表单可以协调一组数据绑定控件，跟踪变更，验证输入，并表达错误信息。" target="_self"><span>简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/reactive-forms" title="使用 FormBuilder、表单组和表单数组创建响应式表单。" target="_self"><span>响应式表单</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/forms" title="使用指令和 Angular 模板语法创建模板驱动表单。" target="_self"><span>模板驱动表单</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/form-validation" title="验证用户的表单输入" target="_self"><span>表单验证</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dynamic-form" title="使用 FormGroup 渲染动态表单。" target="_self"><span>动态表单</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="Observable 与 RxJS" aria-pressed="false"><span>Observable 与 RxJS</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/observables" title="" target="_self"><span>可观察对象(Observable)</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/rx-library" title="" target="_self"><span>RxJS 库</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/observables-in-angular" title="" target="_self"><span>Angular 中的可观察对象</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/practical-observable-usage" title="" target="_self"><span>用法实战</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/comparing-observables" title="" target="_self"><span>与其它技术的比较</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="Angular 中的模块" aria-pressed="false"><span>NgModules</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ngmodules" title="使用 NgModule 让你的应用更高效" target="_self"><span>NgModule 简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ngmodule-vs-jsmodule" title="JavaScript 模块和 NgModule 之间的差异" target="_self"><span>JS 模块 vs NgModule</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/bootstrapping" title="告诉 Angular 如何在根 &quot;AppModule&quot; 中构造和引导应用。" target="_self"><span>应用的根模块</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/frequent-ngmodules" title="介绍最常用的 NgModule" target="_self"><span>常用模块</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/module-types" title="介绍特性模块的几种类型" target="_self"><span>特性模块的分类</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/entry-components" title="关于 Angular 中入口组件的一切" target="_self"><span>入口组件</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/feature-modules" title="创建特性模块，以组织你的代码" target="_self"><span>特性模块</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/providers" title="服务提供商与 NgModule" target="_self"><span>服务提供商</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/singleton-services" title="创建单例服务" target="_self"><span>单例服务</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/lazy-loading-ngmodules" title="惰性加载模块，以提高应用的性能" target="_self"><span>惰性加载的特性模块</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/sharing-ngmodules" title="共享 NgModule 让你的应用现代化。" target="_self"><span>共享 NgModule</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ngmodule-api" title="理解 NgModule 的那些细节。" target="_self"><span>NgModule API</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ngmodule-faq" title="回答关于 NgModules 的常见问题。" target="_self"><span>NgModule 常见问题</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="依赖注入：创建并注入各种服务。" aria-pressed="false"><span>依赖注入</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dependency-injection" title="Angular 的依赖注入系统能够为 Angular 创建的类创建并交付它们所依赖的服务。" target="_self"><span>Angular 依赖注入</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/hierarchical-dependency-injection" title="与组件树平行的注入器树，并支持嵌套的依赖。" target="_self"><span>多级注入器</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dependency-injection-providers" title="各种提供商类型的更多知识。" target="_self"><span>DI 提供商</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dependency-injection-in-action" title="依赖注入的使用技巧" target="_self"><span>DI 实战</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/dependency-injection-navtree" title="使用注入器树来查找父组件。" target="_self"><span>浏览组件树</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/http" title="通过 HTTP 协议与远程服务器对话。" target="_self"><span>HttpClient</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/router" title="揭示如何通过 Angular 路由进行基本的屏幕导航。" target="_self"><span>路由与导航</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="Angular 动画系统指南" aria-pressed="false"><span>动画</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/animations" title="Angular 动画的基础技术。" target="_self"><span>简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/transition-and-triggers" title="转场与触发器的高级技术。" target="_self"><span>转场与触发器</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/complex-animation-sequences" title="复杂的 Angular 动画序列。" target="_self"><span>复杂序列</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/reusable-animations" title="创建可复用的动画。" target="_self"><span>可复用动画</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/route-animations" title="为路由提供转场动画。" target="_self"><span>路由转场动画</span></a></div></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-1 expanded selected ng-star-inserted" title="把 Angular 用到你的实际工作中的一些技巧" aria-pressed="true"><span>其它技术</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 expanded selected"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/security" title="Angular 应用开发中的安全技术。" target="_self"><span>安全</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/i18n" title="把应用模板中的文本翻译成多种语言。" target="_self"><span>国际化（i18n）</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/accessibility" title="设计能被所有用户访问的应用" target="_self"><span>无障碍（a11y）</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="Angular Service Worker: 控制应用资源的缓存。" aria-pressed="false"><span>Service Worker 与 PWA</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-intro" title="Angular 对 Service Worker 的实现提升了慢速或不稳定的网络连接下的用户体验。" target="_self"><span>简介</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-getting-started" title="在 CLI 项目中启用 Service Worker，并在浏览器中查看效果。" target="_self"><span>快速上手</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/app-shell" title="在 CLI 项目中启用应用外壳。" target="_self"><span>应用外壳</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-communications" title="那些能让你和 Angular 的 Service Worker 通讯的服务类。" target="_self"><span>与 Service Worker 通讯</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-devops" title="使用 Service Worker 运行应用、管理应用更新、调试以及杀掉正在运行的应用。" target="_self"><span>生产环境下的 Service Worker</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/service-worker-config" title="配置 Service Worker 的缓存行为。" target="_self"><span>Service Worker 配置</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/universal" title="使用 Angular Universal 在服务端渲染 HTML。" target="_self"><span>服务端渲染</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="把 AngularJS 应用增量式的升级到 Angular。" aria-pressed="false"><span>从 AngularJS 升级</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/upgrade" title="把 AngularJS 应用增量式的升级到 Angular。" target="_self"><span>升级步骤</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/upgrade-performance" title="用更灵活的方式把 AngularJS 升级到 Angular。" target="_self"><span>更关注性能的升级方式</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/ajs-quick-reference" title="学习如何把 AngularJS 的概念映射到 Angular 中。" target="_self"><span>AngularJS 与 Angular 的概念对照</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="使用共享库扩展 Angular" aria-pressed="false"><span>开发 Angular 库</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/libraries" title="理解何时以及如何使用和创建库。" target="_self"><span>库概览</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/using-libraries" title="把已发布的库集成进你的应用中。" target="_self"><span>使用已发布的库</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/creating-libraries" title="通过创建、发布和使用你自己的库来扩展 Angular。" target="_self"><span>创建库</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="理解原理图" aria-pressed="false"><span>原理图（Schematic）</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/schematics" title="理解 Angular 如何使用原理图。" target="_self"><span>原理图概览</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/schematics-authoring" title="理解原理图的结构。" target="_self"><span>制作原理图</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/schematics-for-libraries" title="使用原理图来把你的库集成进 Angular CLI 中。" target="_self"><span>库的原理图</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 expanded selected ng-star-inserted" style="position:relative" href="guide/cli-builder" title="使用构建器定制 Angular CLI。" target="_self"><span>CLI 构建器</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/web-worker" title="在 Angular CLI 中使用 Web Worker。" target="_self"><span>Web Worker</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-1 collapsed ng-star-inserted" title="关于构建、测试和部署的信息" aria-pressed="false"><span>开发工作流</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="理解 AOT 预先编译器。" aria-pressed="false"><span>AOT 预先编译器</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/aot-compiler" title="学习为何以及如何使用预先编译器。" target="_self"><span>预先编译</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/angular-compiler-options" title="配置 AOT 编译。" target="_self"><span>Angular 编译器选项</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/aot-metadata-errors" title="解决 AOT 编译错误。" target="_self"><span>AOT 元数据错误</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/template-typecheck" title="Angular 中的模板类型检查。" target="_self"><span>模板类型检查</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/build" title="构建应用及为应用启动开发服务器。" target="_self"><span>构建与运行</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/bazel" title="如何配置你的环境，以利用 Bazel 进行构建和测试。" target="_self"><span>使用 Bazel 进行构建</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/testing" title="测试 Angular 应用的技巧与实践。" target="_self"><span>测试</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/deployment" title="了解如何部署 Angular 应用。" target="_self"><span>发布</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-2 collapsed ng-star-inserted" title="与开发环境和工具集成起来" aria-pressed="false"><span>开发工具集成</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-2 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-3 collapsed ng-star-inserted" style="position:relative" href="guide/language-service" title="使用 Angular 语言服务加速开发。" target="_self"><span>语言服务</span></a></div></aio-nav-item></div></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-1 collapsed ng-star-inserted" title="工作空间与项目的结构，及其配置文件。" aria-pressed="false"><span>配置</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/file-structure" title="Angular 工作区在文件系统中是怎样的。" target="_self"><span>项目文件结构</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/workspace-config" title="&quot;angular.json&quot; 包含供 CLI 命令使用的工作区和项目默认配置。" target="_self"><span>工作区配置</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/npm-packages" title="开发期间和运行期间所需的 npm 包的说明。" target="_self"><span>npm 包</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/typescript-configuration" title="给 Angular 开发者的 TypeScript 配置。" target="_self"><span>TypeScript 配置</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/browser-support" title="浏览器支持与腻子脚本指南。" target="_self"><span>浏览器支持</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-1 collapsed ng-star-inserted" title="Angular 的版本发布实践、更新与升级。" aria-pressed="false"><span>发布信息</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/updating" title="如何把 Angular 应用和库升级到最新版本。" target="_self"><span>保持最新</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/releases" title="Angular 的版本、发布、支持、弃用策略及实践。" target="_self"><span>发布实践</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/updating-to-version-9" title="支持把你的应用从 8 升级到 9。" target="_self"><span>升级到第 9 版</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/deprecations" title="弃用的 Angular API 和特性汇总。" target="_self"><span>弃用清单</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/ivy" title="关于 Angular Ivy 的编译与渲染管道。" target="_self"><span>Angular Ivy</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-1 collapsed ng-star-inserted" title="Angular 语法、编码、术语汇总。" aria-pressed="false"><span>快捷手册</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/cheatsheet" title="关于 Angular 常用编码技术的快速指南。" target="_self"><span>速查表</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/styleguide" title="写出 Angular 风格的程序" target="_self"><span>风格指南</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="guide/glossary" title="Angular 中最重要的词汇的简要定义。" target="_self"><span>词汇表</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><button type="button" class="vertical-menu-item heading level-1 collapsed ng-star-inserted" title="Angular CLI 命令参考手册。" aria-pressed="false"><span>CLI 命令</span><mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><div class="heading-children level-1 collapsed"><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli" title="CLI 工具介绍、命令、语法" target="_self"><span>概览</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/usage-analytics-gathering" title="管理员如何从用户那里收集使用情况分析。" target="_self"><span>使用情况分析</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/add" title="ng add." target="_self"><span>ng add</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/analytics" title="ng analytics." target="_self"><span>ng analytics</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/build" title="ng build." target="_self"><span>ng build</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/config" title="ng config." target="_self"><span>ng config</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/deploy" title="ng deploy." target="_self"><span>ng deploy</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/doc" title="ng doc." target="_self"><span>ng doc</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/e2e" title="ng e2e." target="_self"><span>ng e2e</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/generate" title="ng generate." target="_self"><span>ng generate</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/help" title="ng help." target="_self"><span>ng help</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/lint" title="ng lint." target="_self"><span>ng lint</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/new" title="ng new." target="_self"><span>ng new</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/run" title="ng run." target="_self"><span>ng run</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/serve" title="ng serve." target="_self"><span>ng serve</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/test" title="ng test." target="_self"><span>ng test</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/update" title="ng update." target="_self"><span>ng update</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/version" title="ng version." target="_self"><span>ng version</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-2 collapsed ng-star-inserted" style="position:relative" href="cli/xi18n" title="ng xi18n." target="_self"><span>ng xi18n</span></a></div></aio-nav-item></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="api" title="关于 Angular 中类和值的详细信息。" target="_self"><span>API 参考手册</span></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><div class="mat-divider ng-star-inserted" style="margin:4px 20px;border-top:1px solid #d3d3d3"></div></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://github.com/ng-docs/ng-docs.github.io/issues" title="github 上的中文互助问答区" target="_blank"><span>互助问答</span><mat-icon role="img" class="mat-icon notranslate material-icons mat-icon-no-color ng-star-inserted" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://material.angular.cn" title="Angular Material 组件库的中文文档" target="_blank"><span>官方 Material 组件库</span><mat-icon role="img" class="mat-icon notranslate material-icons mat-icon-no-color ng-star-inserted" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://ng.ant.design/" title="Ant Design 的 Angular 实现，服务于企业级后台产品。" target="_blank"><span>ng-zorro 组件库</span><mat-icon role="img" class="mat-icon notranslate material-icons mat-icon-no-color ng-star-inserted" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://ng.mobile.ant.design/" title="Ant Design Mobile 的 Angular 实现，服务于无线产品。" target="_blank"><span>ng-zorro mobile 组件库</span><mat-icon role="img" class="mat-icon notranslate material-icons mat-icon-no-color ng-star-inserted" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item><aio-nav-item class="ng-star-inserted"><div class="ng-star-inserted"><a class="vertical-menu-item level-1 collapsed ng-star-inserted" style="position:relative" href="https://flutter-io.cn/" title="Flutter 中文文档站" target="_blank"><span>友站：Flutter 中文</span><mat-icon role="img" class="mat-icon notranslate material-icons mat-icon-no-color ng-star-inserted" aria-hidden="true">open_in_new</mat-icon></a></div></aio-nav-item></aio-nav-menu><div class="doc-version ng-tns-c18-1"><aio-select><div class="form-select-menu"><button class="form-select-button"><span><strong></strong></span><span>stable (v9.0.0)</span></button></div></aio-select></div></div></mat-sidenav><div class="cdk-visually-hidden cdk-focus-trap-anchor" aria-hidden="true"></div><mat-sidenav-content cdkscrollable="" class="mat-drawer-content mat-sidenav-content ng-star-inserted" style="margin-left:261px"><main role="main" class="sidenav-content" id="guide-cli-builder"><aio-mode-banner></aio-mode-banner><aio-doc-viewer class=""><div style="opacity:1"><div class="github-links"><a href="https://github.com/angular/angular-cn/edit/aio/aio/content/guide/cli-builder.md?message=docs%3A%20请简述你的修改..." aria-label="提供编辑建议" title="提供编辑建议"><i class="material-icons" aria-hidden="true" role="img">mode_edit</i></a></div><div class="content"><h1 id="angular-cli-builders" translation-result="on">Angular CLI 构建器（Builder）<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#angular-cli-builders"><i class="material-icons">link</i></a></h1><aio-toc class="embedded" ng-version="9.0.0-rc.11"><div class="toc-inner no-print collapsed ng-star-inserted"><button type="button" title="Expand/collapse contents" aria-label="Expand/collapse contents" class="toc-heading embedded secondary ng-star-inserted" aria-pressed="false">目录<mat-icon role="img" svgicon="keyboard_arrow_right" class="mat-icon notranslate rotating-icon collapsed mat-icon-no-color" aria-hidden="true"><svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"></path></svg></mat-icon></button><ul class="toc-list embedded"><li class="h2 ng-star-inserted" title="CLI 构建器"><a href="guide/cli-builder#cli-builders">CLI 构建器</a></li><li class="h3 ng-star-inserted" title="构建器的项目结构"><a href="guide/cli-builder#builder-project-structure">构建器的项目结构</a></li><li class="h2 ng-star-inserted" title="创建构建器"><a href="guide/cli-builder#creating-a-builder">创建构建器</a></li><li class="h3 secondary ng-star-inserted" title="处理输出"><a href="guide/cli-builder#handling-output">处理输出</a></li><li class="h3 secondary ng-star-inserted" title="进度和状态报告"><a href="guide/cli-builder#progress-and-status-reporting">进度和状态报告</a></li><li class="h2 secondary ng-star-inserted" title="构建器的输入"><a href="guide/cli-builder#builder-input">构建器的输入</a></li><li class="h3 secondary ng-star-inserted" title="对输入的验证"><a href="guide/cli-builder#input-validation">对输入的验证</a></li><li class="h3 secondary ng-star-inserted" title="目标配置"><a href="guide/cli-builder#target-configuration">目标配置</a></li><li class="h2 secondary ng-star-inserted" title="调度并运行"><a href="guide/cli-builder#schedule-and-run">调度并运行</a></li><li class="h3 secondary ng-star-inserted" title="默认建筑师配置"><a href="guide/cli-builder#default-architect-configuration">默认建筑师配置</a></li><li class="h3 secondary ng-star-inserted" title="添加一个目标"><a href="guide/cli-builder#adding-a-target">添加一个目标</a></li><li class="h3 secondary ng-star-inserted" title="运行这个构建器"><a href="guide/cli-builder#running-the-builder">运行这个构建器</a></li><li class="h2 secondary ng-star-inserted" title="测试一个构建器"><a href="guide/cli-builder#testing-a-builder">测试一个构建器</a></li><li class="h3 secondary ng-star-inserted" title="监视（watch）模式"><a href="guide/cli-builder#watch-mode">监视（watch）模式</a></li><li class="h2 secondary ng-star-inserted" title="总结"><a href="guide/cli-builder#summary">总结</a></li></ul><button type="button" title="Expand/collapse contents" aria-label="Expand/collapse contents" class="toc-more-items embedded material-icons collapsed ng-star-inserted" aria-pressed="false"></button></div></aio-toc><h1 translation-origin="off" id="angular-cli-builders">Angular CLI builders<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#angular-cli-builders"><i class="material-icons">link</i></a></h1><p translation-result="on">很多 Angular CLI 命令都要在你的代码上执行一些复杂的处理，比如风格检查（lint）构建或测试。这些命令会通过一个叫做建筑师（Architect）的内部工具来运行 <em>CLI 构建器</em>，而这些构建器会运用一些第三方工具来完成目标任务。</p><p translation-origin="off">A number of Angular CLI commands run a complex process on your code, such as linting, building, or testing. The commands use an internal tool called Architect to run <em>CLI builders</em>, which apply another tool to accomplish the desired task.</p><p translation-result="on">在 Angular 的版本 8 中，CLI 构建器的 API 是稳定的，想要通过添加或修改命令来自定义 Angular CLI 的开发人员可以使用它。例如，你可以提供一个构建器来执行全新的任务，或者更改一个现有命令所使用的第三方工具。</p><p translation-origin="off">With Angular version 8, the CLI Builder API is stable and available to developers who want to customize the Angular CLI by adding or modifying commands. For example, you could supply a builder to perform an entirely new task, or to change which third-party tool is used by an existing command.</p><p translation-result="on">本文档介绍了 CLI 构建器是如何与工作区配置文件集成的，还展示了如何创建你自己的构建器。</p><p translation-origin="off">This document explains how CLI builders integrate with the workspace configuration file, and shows how you can create your own builder.</p><div class="alert is-helpful"><p translation-result="on">你可以在<a href="https://github.com/mgechev/cli-builders-demo">这个 GitHub 仓库</a>中的例子中找到代码。</p><p translation-origin="off">You can find the code from the examples used here in <a href="https://github.com/mgechev/cli-builders-demo">this GitHub repository</a>.</p></div><h2 id="cli-builders" translation-result="on">CLI 构建器<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#cli-builders"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="cli-builders">CLI builders<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#cli-builders"><i class="material-icons">link</i></a></h2><p translation-result="on">内部建筑师工具会把工作委托给名叫<a href="guide/glossary#builder"><em>构建器</em></a>的处理器函数。处理器函数接收两个参数：一组 <code>options</code> 输入（JSON 对象）和一个 <code>context</code>（<code>BuilderContext</code> 对象）。</p><p translation-origin="off">The internal Architect tool delegates work to handler functions called <a href="guide/glossary#builder"><em>builders</em></a>. A builder handler function receives two arguments; a set of input <code>options</code> (a JSON object), and a <code>context</code> (a <code>BuilderContext</code> object).</p><p translation-result="on">这里对关注点的分离和<a href="guide/glossary#schematic">原理图</a>中是一样的，它也适用于其它要接触（touch）代码的 CLI 命令（例如 <code>ng generate</code>）。</p><p translation-origin="off">The separation of concerns here is the same as with <a href="guide/glossary#schematic">schematics</a>, which are used for other CLI commands that touch your code (such as <code>ng generate</code>).</p><ul><li><p translation-result="on">选项由 CLI 用户提供，上下文由 CLI 构建器提供，并提供对 CLI 构建器 API 的访问，而开发人员提供了处理函数的行为。</p><p translation-origin="off">Options are given by the CLI user, context is provided by and provides access to the CLI Builder API, and the developer provides the behavior.</p></li><li><p translation-result="on"><code>BuilderContext</code> 对象提供了访问调度方法 <code>BuilderContext.scheduleTarget()</code> 的途径。调度器会用指定的<a href="guide/glossary#target">目标配置</a>来执行构建器处理函数。</p><p translation-origin="off">The <code>BuilderContext</code> object provides access to the scheduling method, <code>BuilderContext.scheduleTarget()</code>. The scheduler executes the builder handler function with a given <a href="guide/glossary#target">target configuration</a>.</p></li></ul><p translation-result="on">这个构建器处理函数可以是同步的（返回一个值）或异步的（返回一个 Promise），也可以监视并返回多个值（返回一个 Observable）。最终返回的值全都是 <code>BuilderOutput</code> 类型的。该对象包含一个逻辑字段 <code>success</code> 和一个可以包含错误信息的可选字段 <code>error</code>。</p><p translation-origin="off">The builder handler function can be synchronous (return a value) or asynchronous (return a Promise), or it can watch and return multiple values (return an Observable). The return value or values must always be of type <code>BuilderOutput</code>. This object contains a Boolean <code>success</code> field and an optional <code>error</code> field that can contain an error message.</p><p translation-result="on">Angular 提供了一些构建器，供 CLI 命令使用，如 <code>ng build</code>、<code>ng test</code> 和 <code>ng lint</code> 等。这些内置 CLI 构建器的默认目标配置可以在<a href="guide/workspace-config">工作空间配置文件</a> <code>angular.json</code> 的 <code>architect</code> 部分找到（并进行自定义）。可以通过创建自己的构建器来扩展和自定义 Angular，你可以使用 <a href="cli/run"><code>ng run</code> CLI 命令</a>来运行你自己的构建器。</p><p translation-origin="off">Angular provides some builders that are used by the CLI for commands such as <code>ng build</code>, <code>ng test</code>, and <code>ng lint</code>. Default target configurations for these and other built-in CLI builders can be found (and customized) in the "architect" section of the <a href="guide/workspace-config">workspace configuration file</a>, <code>angular.json</code>. You can also extend and customize Angular by creating your own builders, which you can run using the <a href="cli/run"><code>ng run</code> CLI command</a>.</p><h3 id="builder-project-structure" translation-result="on">构建器的项目结构<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#builder-project-structure"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="builder-project-structure">Builder project structure<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#builder-project-structure"><i class="material-icons">link</i></a></h3><p translation-result="on">构建器位于一个 <code>project</code> 文件夹中，该文件夹的结构类似于 Angular 工作区，包括位于顶级的全局配置文件，以及位于工作代码所在源文件夹中的更具体的配置。例如，<code>myBuilder</code> 文件夹中可能包含如下文件。</p><p translation-origin="off">A builder resides in a "project" folder that is similar in structure to an Angular workspace, with global configuration files at the top level, and more specific configuration in a source folder with the code files that define the behavior. For example, your <code>myBuilder</code> folder could contain the following files.</p><table><thead><tr><th align="left"><p translation-result="on">文件</p><p translation-origin="off">FILES</p></th><th align="left"><p translation-result="on">目的</p><p translation-origin="off">PURPOSE</p></th></tr></thead><tbody><tr><td align="left"><code>src/my-builder.ts</code></td><td align="left"><p translation-result="on">这个构建器定义的主要源码。</p><p translation-origin="off">Main source file for the builder definition.</p></td></tr><tr><td align="left"><code>src/my-builder.spec.ts</code></td><td align="left"><p translation-result="on">测试的源码。</p><p translation-origin="off">Source file for tests.</p></td></tr><tr><td align="left"><code>src/schema.json</code></td><td align="left"><p translation-result="on">构建器输入选项的定义。</p><p translation-origin="off">Definition of builder input options.</p></td></tr><tr><td align="left"><code>builders.json</code></td><td align="left"><p translation-result="on">测试配置。</p><p translation-origin="off">Testing configuration.</p></td></tr><tr><td align="left"><code>package.json</code></td><td align="left"><p translation-result="on">依赖包。参见<a href="https://docs.npmjs.com/files/package.json">https://docs.npmjs.com/files/package.json</a> 。</p><p translation-origin="off">Dependencies. See <a href="https://docs.npmjs.com/files/package.json">https://docs.npmjs.com/files/package.json</a>.</p></td></tr><tr><td align="left"><code>tsconfig.json</code></td><td align="left"><p translation-result="on"><a href="https://www.typescriptlang.org/docs/handbook/tsconfig-json.html">TypeScript 配置文件</a></p><p translation-origin="off"><a href="https://www.typescriptlang.org/docs/handbook/tsconfig-json.html">TypeScript configuration</a>.</p></td></tr></tbody></table><p translation-result="on">你可以把构建器发布到 <code>npm</code>（请参阅<a href="https://angular.io/guide/creating-libraries#publishing-your-library">发布你的库</a>）。如果把它发布成了 <code>@example/my-builder</code>，就可以使用下面的命令来安装它。</p><p translation-origin="off">You can publish the builder to <code>npm</code> (see <a href="https://angular.io/guide/creating-libraries#publishing-your-library">Publishing your Library</a>). If you publish it as <code>@example/my-builder</code>, you can install it using the following command.</p><code-example language="sh" ng-version="9.0.0-rc.11"><div style="display:none">npm install @example/my-builder</div><aio-code><pre class="lang-sh prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">npm install </span><span class="lit">@example</span><span class="pun">/</span><span class="pln">my</span><span class="pun">-</span><span class="pln">builder</span></code>
    </pre></aio-code></code-example><h2 id="creating-a-builder" translation-result="on">创建构建器<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#creating-a-builder"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="creating-a-builder">Creating a builder<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#creating-a-builder"><i class="material-icons">link</i></a></h2><p translation-result="on">举个例子，让我们创建一个用来执行 shell 命令的构建器。要创建构建器，请使用 CLI 构建器函数 <code>createBuilder()</code>，并返回一个 <code>Promise&lt;BuilderOutput&gt;</code> 对象。</p><p translation-origin="off">As an example, let's create a builder that executes a shell command. To create a builder, use the <code>createBuilder()</code> CLI Builder function, and return a <code>Promise&lt;BuilderOutput&gt;</code> object.</p><code-example path="cli-builder/src/my-builder.ts" header="src/my-builder.ts (builder skeleton)" region="builder-skeleton" ng-version="9.0.0-rc.11"><div style="display:none">import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'; import { JsonObject } from '@angular-devkit/core'; interface Options extends JsonObject { command: string; args: string[]; } export default createBuilder(commandBuilder); function commandBuilder( options: Options, context: BuilderContext, ): Promise&lt;BuilderOutput&gt; { }</div><header class="ng-star-inserted">src/my-builder.ts (builder skeleton)</header><aio-code class="headed-code"><pre class="lang- prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from src/my-builder.ts (builder skeleton)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">BuilderContext</span><span class="pun">,</span><span class="pln"> </span><span class="typ">BuilderOutput</span><span class="pun">,</span><span class="pln"> createBuilder </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/architect'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">JsonObject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/core'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">interface</span><span class="pln"> </span><span class="typ">Options</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">JsonObject</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  command</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">;</span><span class="pln">
  args</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">[];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> createBuilder</span><span class="pun">(</span><span class="pln">commandBuilder</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> commandBuilder</span><span class="pun">(</span><span class="pln">
  options</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Options</span><span class="pun">,</span><span class="pln">
  context</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BuilderContext</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">&lt;</span><span class="typ">BuilderOutput</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">现在，让我们为它添加一些逻辑。下列代码会从用户选项中检索命令和参数、生成新进程，并等待该进程完成。如果进程成功（返回代码为 0），就会解析成返回的值。</p><p translation-origin="off">Now let’s add some logic to it. The following code retrieves the command and arguments from the user options, spawns the new process, and waits for the process to finish. If the process is successful (returns a code of 0), it resolves the return value.</p><code-example path="cli-builder/src/my-builder.ts" header="src/my-builder.ts (builder)" region="builder" ng-version="9.0.0-rc.11"><div style="display:none">import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'; import { JsonObject } from '@angular-devkit/core'; import * as childProcess from 'child_process'; interface Options extends JsonObject { command: string; args: string[]; } export default createBuilder(commandBuilder); function commandBuilder( options: Options, context: BuilderContext, ): Promise&lt;BuilderOutput&gt; { const child = childProcess.spawn(options.command, options.args); return new Promise(<a href="api/router/Route#resolve" class="code-anchor">resolve</a> =&gt; { child.on('close', code =&gt; { <a href="api/router/Route#resolve" class="code-anchor">resolve</a>({ success: code === 0 }); }); }); }</div><header class="ng-star-inserted">src/my-builder.ts (builder)</header><aio-code class="headed-code"><pre class="lang- prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from src/my-builder.ts (builder)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">BuilderContext</span><span class="pun">,</span><span class="pln"> </span><span class="typ">BuilderOutput</span><span class="pun">,</span><span class="pln"> createBuilder </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/architect'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">JsonObject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> childProcess </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'child_process'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">interface</span><span class="pln"> </span><span class="typ">Options</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">JsonObject</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  command</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">;</span><span class="pln">
  args</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">[];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> createBuilder</span><span class="pun">(</span><span class="pln">commandBuilder</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> commandBuilder</span><span class="pun">(</span><span class="pln">
  options</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Options</span><span class="pun">,</span><span class="pln">
  context</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BuilderContext</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">&lt;</span><span class="typ">BuilderOutput</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> childProcess</span><span class="pun">.</span><span class="pln">spawn</span><span class="pun">(</span><span class="pln">options</span><span class="pun">.</span><span class="pln">command</span><span class="pun">,</span><span class="pln"> options</span><span class="pun">.</span><span class="pln">args</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><a href="api/router/Route#resolve" class="code-anchor"><span class="pln">resolve</span></a><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      child</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> code </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><a href="api/router/Route#resolve" class="code-anchor"><span class="pln">resolve</span></a><span class="pun">({</span><span class="pln"> success</span><span class="pun">:</span><span class="pln"> code </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
      </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><h3 id="handling-output" translation-result="on">处理输出<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#handling-output"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="handling-output">Handling output<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#handling-output"><i class="material-icons">link</i></a></h3><p translation-result="on">默认情况下，<code>spawn()</code> 方法会把所有内容输出到进程标准输出（stdout）和标准错误（stderr）中。为了便于测试和调试，我们可以把输出转发给 CLI 构建器的 Logger。这样还能让构建器本身可以在一个单独的进程中执行，即使其标准输出和标准错误被停用了也无所谓（就像在 <a href="https://electronjs.org/">Electron 应用中一样</a>）。</p><p translation-origin="off">By default, the <code>spawn()</code> method outputs everything to the process standard output and error. To make it easier to test and debug, we can forward the output to the CLI Builder logger instead. This also allows the builder itself to be executed in a separate process, even if the standard output and error are deactivated (as in an <a href="https://electronjs.org/">Electron app</a>).</p><p translation-result="on">我们可以从上下文中检索一个 Logger 实例。</p><p translation-origin="off">We can retrieve a Logger instance from the context.</p><code-example path="cli-builder/src/my-builder.ts" header="src/my-builder.ts (handling output)" region="handling-output" ng-version="9.0.0-rc.11"><div style="display:none">import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'; import { JsonObject } from '@angular-devkit/core'; import * as childProcess from 'child_process'; interface Options extends JsonObject { command: string; args: string[]; } export default createBuilder(commandBuilder); function commandBuilder( options: Options, context: BuilderContext, ): Promise&lt;BuilderOutput&gt; { const child = childProcess.spawn(options.command, options.args); child.stdout.on('data', data =&gt; { context.logger.info(data.toString()); }); child.stderr.on('data', data =&gt; { context.logger.error(data.toString()); }); return new Promise(<a href="api/router/Route#resolve" class="code-anchor">resolve</a> =&gt; { child.on('close', code =&gt; { <a href="api/router/Route#resolve" class="code-anchor">resolve</a>({ success: code === 0 }); }); }); }</div><header class="ng-star-inserted">src/my-builder.ts (handling output)</header><aio-code class="headed-code"><pre class="lang- prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from src/my-builder.ts (handling output)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">BuilderContext</span><span class="pun">,</span><span class="pln"> </span><span class="typ">BuilderOutput</span><span class="pun">,</span><span class="pln"> createBuilder </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/architect'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">JsonObject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> childProcess </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'child_process'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">interface</span><span class="pln"> </span><span class="typ">Options</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">JsonObject</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  command</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">;</span><span class="pln">
  args</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">[];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> createBuilder</span><span class="pun">(</span><span class="pln">commandBuilder</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> commandBuilder</span><span class="pun">(</span><span class="pln">
  options</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Options</span><span class="pun">,</span><span class="pln">
  context</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BuilderContext</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">&lt;</span><span class="typ">BuilderOutput</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> childProcess</span><span class="pun">.</span><span class="pln">spawn</span><span class="pun">(</span><span class="pln">options</span><span class="pun">.</span><span class="pln">command</span><span class="pun">,</span><span class="pln"> options</span><span class="pun">.</span><span class="pln">args</span><span class="pun">);</span><span class="pln">

    child</span><span class="pun">.</span><span class="pln">stdout</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> data </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      context</span><span class="pun">.</span><span class="pln">logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
    child</span><span class="pun">.</span><span class="pln">stderr</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> data </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      context</span><span class="pun">.</span><span class="pln">logger</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><a href="api/router/Route#resolve" class="code-anchor"><span class="pln">resolve</span></a><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      child</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> code </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><a href="api/router/Route#resolve" class="code-anchor"><span class="pln">resolve</span></a><span class="pun">({</span><span class="pln"> success</span><span class="pun">:</span><span class="pln"> code </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
      </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><h3 id="progress-and-status-reporting" translation-result="on">进度和状态报告<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#progress-and-status-reporting"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="progress-and-status-reporting">Progress and status reporting<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#progress-and-status-reporting"><i class="material-icons">link</i></a></h3><p translation-result="on">CLI 构建器 API 包含一些进度报告和状态报告工具，可以为某些函数和接口提供提示信息。</p><p translation-origin="off">The CLI Builder API includes progress and status reporting tools, which can provide hints for certain functions and interfaces.</p><p translation-result="on">要报告进度，请使用 <code>BuilderContext.reportProgress()</code> 方法，它接受一个当前值（value）、一个（可选的）总值（total）和状态（status）字符串作为参数。总值可以是任意数字，例如，如果你知道有多少个文件需要处理，那么总值可能是这些文件的数量，而当前值是已处理过的数量。除非传入了新的字符串，否则这个状态字符串不会改变。</p><p translation-origin="off">To report progress, use the <code>BuilderContext.reportProgress()</code> method, which takes a current value, (optional) total, and status string as arguments. The total can be any number; for example, if you know how many files you have to process, the total could be the number of files, and current should be the number processed so far. The status string is unmodified unless you pass in a new string value.</p><p translation-result="on">你可以看看 <code>tslint</code> 构建器如何报告进度的<a href="https://github.com/angular/angular-cli/blob/ba21c855c0c8b778005df01d4851b5a2176edc6f/packages/angular_devkit/build_angular/src/tslint/index.ts#L107">例子</a> 。</p><p translation-origin="off">You can see an <a href="https://github.com/angular/angular-cli/blob/ba21c855c0c8b778005df01d4851b5a2176edc6f/packages/angular_devkit/build_angular/src/tslint/index.ts#L107">example</a> of how the <code>tslint</code> builder reports progress.</p><p translation-result="on">在我们的例子中，shell 命令或者已完成或者正在执行，所以不需要进度报告，但是可以报告状态，以便调用此构建器的父构建器知道发生了什么。可以用 <code>BuilderContext.reportStatus()</code> 方法生成一个任意长度的状态字符串。（注意，无法保证长字符串会完全显示出来，可以裁剪它以适应界面显示。）传入一个空字符串可以移除状态。</p><p translation-origin="off">In our example, the shell command either finishes or is still executing, so there’s no need for a progress report, but we can report status so that a parent builder that called our builder would know what’s going on. Use the <code>BuilderContext.reportStatus()</code> method to generate a status string of any length. (Note that there’s no guarantee that a long string will be shown entirely; it could be cut to fit the UI that displays it.) Pass an empty string to remove the status.</p><code-example path="cli-builder/src/my-builder.ts" header="src/my-builder.ts (progess reporting)" region="progress-reporting" ng-version="9.0.0-rc.11"><div style="display:none">import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'; import { JsonObject } from '@angular-devkit/core'; import * as childProcess from 'child_process'; interface Options extends JsonObject { command: string; args: string[]; } export default createBuilder(commandBuilder); function commandBuilder( options: Options, context: BuilderContext, ): Promise&lt;BuilderOutput&gt; { context.reportStatus(`Executing "${options.command}"...`); const child = childProcess.spawn(options.command, options.args); child.stdout.on('data', data =&gt; { context.logger.info(data.toString()); }); child.stderr.on('data', data =&gt; { context.logger.error(data.toString()); }); return new Promise(<a href="api/router/Route#resolve" class="code-anchor">resolve</a> =&gt; { context.reportStatus(`Done.`); child.on('close', code =&gt; { <a href="api/router/Route#resolve" class="code-anchor">resolve</a>({ success: code === 0 }); }); }); }</div><header class="ng-star-inserted">src/my-builder.ts (progess reporting)</header><aio-code class="headed-code"><pre class="lang- prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from src/my-builder.ts (progess reporting)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">BuilderContext</span><span class="pun">,</span><span class="pln"> </span><span class="typ">BuilderOutput</span><span class="pun">,</span><span class="pln"> createBuilder </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/architect'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">JsonObject</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/core'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> childProcess </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'child_process'</span><span class="pun">;</span><span class="pln">

</span><span class="kwd">interface</span><span class="pln"> </span><span class="typ">Options</span><span class="pln"> </span><span class="kwd">extends</span><span class="pln"> </span><span class="typ">JsonObject</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  command</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">;</span><span class="pln">
  args</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">[];</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="kwd">export</span><span class="pln"> </span><span class="kwd">default</span><span class="pln"> createBuilder</span><span class="pun">(</span><span class="pln">commandBuilder</span><span class="pun">);</span><span class="pln">

</span><span class="kwd">function</span><span class="pln"> commandBuilder</span><span class="pun">(</span><span class="pln">
  options</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Options</span><span class="pun">,</span><span class="pln">
  context</span><span class="pun">:</span><span class="pln"> </span><span class="typ">BuilderContext</span><span class="pun">,</span><span class="pln">
  </span><span class="pun">):</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">&lt;</span><span class="typ">BuilderOutput</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    context</span><span class="pun">.</span><span class="pln">reportStatus</span><span class="pun">(</span><span class="str">`Executing "${options.command}"...`</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> childProcess</span><span class="pun">.</span><span class="pln">spawn</span><span class="pun">(</span><span class="pln">options</span><span class="pun">.</span><span class="pln">command</span><span class="pun">,</span><span class="pln"> options</span><span class="pun">.</span><span class="pln">args</span><span class="pun">);</span><span class="pln">

    child</span><span class="pun">.</span><span class="pln">stdout</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> data </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      context</span><span class="pun">.</span><span class="pln">logger</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
    child</span><span class="pun">.</span><span class="pln">stderr</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'data'</span><span class="pun">,</span><span class="pln"> data </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      context</span><span class="pun">.</span><span class="pln">logger</span><span class="pun">.</span><span class="pln">error</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">toString</span><span class="pun">());</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">

    </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun">(</span><a href="api/router/Route#resolve" class="code-anchor"><span class="pln">resolve</span></a><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      context</span><span class="pun">.</span><span class="pln">reportStatus</span><span class="pun">(</span><span class="str">`Done.`</span><span class="pun">);</span><span class="pln">
      child</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'close'</span><span class="pun">,</span><span class="pln"> code </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><a href="api/router/Route#resolve" class="code-anchor"><span class="pln">resolve</span></a><span class="pun">({</span><span class="pln"> success</span><span class="pun">:</span><span class="pln"> code </span><span class="pun">===</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">});</span><span class="pln">
      </span><span class="pun">});</span><span class="pln">
    </span><span class="pun">});</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><h2 id="builder-input" translation-result="on">构建器的输入<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#builder-input"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="builder-input">Builder input<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#builder-input"><i class="material-icons">link</i></a></h2><p translation-result="on">你可以通过 CLI 命令间接调用一个构建器，也可以直接用 Angular CLI 的 <code>ng run</code> 命令来调用它。无论哪种情况，你都必须提供所需的输入，但是可以用特定<a href="guide/glossary#target"><em>目标</em></a>中预配置的值作为其默认值，然后指定一个预定义的、指定的配置进行覆盖，最后在命令行中进一步覆盖这些选项的值。</p><p translation-origin="off">You can invoke a builder indirectly through a CLI command, or directly with the Angular CLI <code>ng run</code> command. In either case, you must provide required inputs, but can allow other inputs to default to values that are pre-configured for a specific <a href="guide/glossary#target"><em>target</em></a>, provide a pre-defined, named override configuration, and provide further override option values on the command line.</p><h3 id="input-validation" translation-result="on">对输入的验证<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#input-validation"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="input-validation">Input validation<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#input-validation"><i class="material-icons">link</i></a></h3><p translation-result="on">你可以在该构建器的相关 JSON 模式中定义构建器都有哪些输入。 建筑师工具会把解析后的输入值收集到一个 <code>options</code> 对象中，并在将其传给构建器函数之前先根据这个模式验证它们的类型。（Schematics 库也对用户输入做了同样的验证）。</p><p translation-origin="off">You define builder inputs in a JSON schema associated with that builder. The Architect tool collects the resolved input values into an <code>options</code> object, and validates their types against the schema before passing them to the builder function. (The Schematics library does the same kind of validation of user input).</p><p translation-result="on">对于这个示例构建器，我们希望 <code>options</code> 值是带有两个键的 <code>JsonObject</code>：一个是字符串型的 <code>command</code> ，一个是字符串数组型的 <code>args</code>。</p><p translation-origin="off">For our example builder, we expect the <code>options</code> value to be a <code>JsonObject</code> with two keys: a <code>command</code> that is a string, and an <code>args</code> array of string values.</p><p translation-result="on">我们可以提供如下模式来对这些值的类型进行验证。</p><p translation-origin="off">We can provide the following schema for type validation of these values.</p><code-example language="json" header="command/schema.json" ng-version="9.0.0-rc.11"><div style="display:none">{ "$schema": "http://json-schema.org/schema", "type": "object", "properties": { "command": { "type": "string" }, "args": { "type": "array", "items": { "type": "string" } } } }</div><header class="ng-star-inserted">command/schema.json</header><aio-code class="headed-code"><pre class="lang-json prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from command/schema.json">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pun">{</span><span class="pln">
  </span><span class="str">"$schema"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"http://json-schema.org/schema"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"object"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"properties"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"command"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"string"</span><span class="pln">
    </span><span class="pun">},</span><span class="pln">
    </span><span class="str">"args"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"array"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"items"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"string"</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><div class="alert is-helpful"><p translation-result="on">这是一个非常简单的例子，但这种模式验证也可以非常强大。要了解更多信息，请参阅 <a href="http://json-schema.org/">JSON 模式网站</a> 。</p><p translation-origin="off">This is a very simple example, but the use of a schema for validation can be very powerful. For more information, see the <a href="http://json-schema.org/">JSON schemas website</a>.</p></div><p translation-result="on">要把构建器的实现与它的模式和名称关联起来，我们需要创建一个<em>构建器定义</em>文件，可以在 <code>package.json</code> 中指向该文件。</p><p translation-origin="off">To link our builder implementation with its schema and name, we need to create a <em>builder definition</em> file, which we can point to in <code>package.json</code>.</p><p translation-result="on">创建一个名为 <code>builders.json</code> 文件，它看起来像这样。</p><p translation-origin="off">Create a file named <code>builders.json</code> file that looks like this.</p><code-example language="json" header="builders.json" ng-version="9.0.0-rc.11"><div style="display:none">{ "builders": { "command": { "implementation": "./command", "schema": "./command/schema.json", "description": "Runs any command line in the operating system." } } }</div><header class="ng-star-inserted">builders.json</header><aio-code class="headed-code"><pre class="lang-json prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from builders.json">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pun">{</span><span class="pln">
  </span><span class="str">"builders"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"command"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"implementation"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"./command"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"schema"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"./command/schema.json"</span><span class="pun">,</span><span class="pln">
      </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Runs any command line in the operating system."</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">在 <code>package.json</code> 文件中，添加一个 <code>builders</code> 键，告诉建筑师工具可以在哪里找到这个构建器定义文件。</p><p translation-origin="off">In the <code>package.json</code> file, add a <code>builders</code> key that tells the Architect tool where to find our builder definition file.</p><code-example language="json" header="package.json" ng-version="9.0.0-rc.11"><div style="display:none">{ "name": "@example/command-runner", "version": "1.0.0", "description": "Builder for Command Runner", "builders": "builders.json", "devDependencies": { "@angular-devkit/architect": "^1.0.0" } }</div><header class="ng-star-inserted">package.json</header><aio-code class="headed-code"><pre class="lang-json prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from package.json">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pun">{</span><span class="pln">
  </span><span class="str">"name"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"@example/command-runner"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"version"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"1.0.0"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"description"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"Builder for Command Runner"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"builders"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"builders.json"</span><span class="pun">,</span><span class="pln">
  </span><span class="str">"devDependencies"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"@angular-devkit/architect"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"^1.0.0"</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><p translation-result="on">现在，这个构建器的正式名字是 <code>@example/command-runner:command</code>。第一部分是包名（使用 node 方案进行解析），第二部分是构建器名称（使用 <code>builders.json</code> 文件进行解析）。</p><p translation-origin="off">The official name of our builder is now <code>@example/command-runner:command</code>. The first part of this is the package name (resolved using node resolution), and the second part is the builder name (resolved using the <code>builders.json</code> file).</p><p translation-result="on">使用某个 <code>options</code> 是非常简单的。在上一节，我们就曾访问过 <code>options.command</code>。</p><p translation-origin="off">Using one of our <code>options</code> is very straightforward, we did this in the previous section when we accessed <code>options.command</code>.</p><code-example path="cli-builder/src/my-builder.ts" header="src/my-builder.ts (report status)" region="report-status" ng-version="9.0.0-rc.11"><div style="display:none">context.reportStatus(`Executing "${options.command}"...`); const child = childProcess.spawn(options.command, options.args);</div><header class="ng-star-inserted">src/my-builder.ts (report status)</header><aio-code class="headed-code"><pre class="lang- prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from src/my-builder.ts (report status)">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">context</span><span class="pun">.</span><span class="pln">reportStatus</span><span class="pun">(</span><span class="str">`Executing "${options.command}"...`</span><span class="pun">);</span><span class="pln">
</span><span class="kwd">const</span><span class="pln"> child </span><span class="pun">=</span><span class="pln"> childProcess</span><span class="pun">.</span><span class="pln">spawn</span><span class="pun">(</span><span class="pln">options</span><span class="pun">.</span><span class="pln">command</span><span class="pun">,</span><span class="pln"> options</span><span class="pun">.</span><span class="pln">args</span><span class="pun">);</span></code>
    </pre></aio-code></code-example><h3 id="target-configuration" translation-result="on">目标配置<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#target-configuration"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="target-configuration">Target configuration<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#target-configuration"><i class="material-icons">link</i></a></h3><p translation-result="on">构建器必须有一个已定义的目标，此目标会把构建器与特定的输入配置和<a href="guide/glossary#project">项目</a>关联起来。</p><p translation-origin="off">A builder must have a defined target that associates it with a specific input configuration and <a href="guide/glossary#project">project</a>.</p><p translation-result="on">目标是在 <a href="guide/workspace-config">CLI 配置文件</a> <code>angular.json</code> 中定义的。目标用于指定要使用的构建器、默认的选项配置，以及指定的备用配置。建筑师工具使用目标定义来为一次特定的执行解析输入选项。</p><p translation-origin="off">Targets are defined in the <code>angular.json</code> <a href="guide/workspace-config">CLI configuration file</a>. A target specifies the builder to use, its default options configuration, and named alternative configurations. The Architect tool uses the target definition to resolve input options for a given run.</p><p translation-result="on"><code>angular.json</code> 文件中为每个项目都有一节配置，每个项目的 <code>architect</code> 部分都会为 CLI 命令（例如 <code>build</code>、<code>test</code> 和 <code>lint</code>）配置构建器目标。默认情况下，<code>build</code> 命令会运行 <code>@angular-devkit/build-angular:<a href="api/animations/browser" class="code-anchor">browser</a></code> 构建器来执行 <code>build</code> 任务，并传入 <code>angular.json</code> 中为 <code>build</code> 目标指定的默认选项值。</p><p translation-origin="off">The <code>angular.json</code> file has a section for each project, and the "architect" section of each project configures targets for builders used by CLI commands such as 'build', 'test', and 'lint'. By default, for example, the <code>build</code> command runs the builder <code>@angular-devkit/build-angular:<a href="api/animations/browser" class="code-anchor">browser</a></code> to perform the build task, and passes in default option values as specified for the <code>build</code> target in <code>angular.json</code>.</p><code-example language="json" header="angular.json" ng-version="9.0.0-rc.11"><div style="display:none">{ "myApp": { ... "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/myApp", "index": "src/index.html", ... }, "configurations": { "production": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "optimization": true, "outputHashing": "all", ... } } }, ...</div><header class="ng-star-inserted">angular.json</header><aio-code class="headed-code"><pre class="lang-json prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from angular.json">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pun">{</span><span class="pln">
  </span><span class="str">"myApp"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">...</span><span class="pln">
    </span><span class="str">"architect"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"build"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"builder"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"@angular-devkit/build-angular:browser"</span><span class="pun">,</span><span class="pln">
        </span><span class="str">"options"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="str">"outputPath"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"dist/myApp"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"index"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/index.html"</span><span class="pun">,</span><span class="pln">
          </span><span class="pun">...</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
        </span><span class="str">"configurations"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="str">"production"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">"fileReplacements"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
              </span><span class="pun">{</span><span class="pln">
                </span><span class="str">"replace"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/environments/environment.ts"</span><span class="pun">,</span><span class="pln">
                </span><span class="str">"with"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/environments/environment.prod.ts"</span><span class="pln">
              </span><span class="pun">}</span><span class="pln">
            </span><span class="pun">],</span><span class="pln">
            </span><span class="str">"optimization"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"outputHashing"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"all"</span><span class="pun">,</span><span class="pln">
            </span><span class="pun">...</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">},</span><span class="pln">
      </span><span class="pun">...</span></code>
    </pre></aio-code></code-example><p translation-result="on">该命令会给构建器传递 <code>options</code> 节中指定的一组默认选项。如果你传入了 <code>--configuration=production</code> 标志，它就会使用 <code>production</code> 备用配置中指定的值进行覆盖。你可以在命令行中单独指定其它选项进行覆盖，还可以为 <code>build</code> 目标添加更多备用配置，以定义其它环境，比如 <code>stage</code> 或 <code>qa</code>。</p><p translation-origin="off">The command passes the builder the set of default options specified in the "options" section. If you pass the <code>--configuration=production</code> flag, it uses the override values specified in the <code>production</code> alternative configuration. You can specify further option overrides individually on the command line. You might also add more alternative configurations to the <code>build</code> target, to define other environments such as <code>stage</code> or <code>qa</code>.</p><h4 id="target-strings" translation-result="on">目标字符串<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#target-strings"><i class="material-icons">link</i></a></h4><h4 translation-origin="off" id="target-strings">Target strings<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#target-strings"><i class="material-icons">link</i></a></h4><p translation-result="on">通用的 <code>ng run</code> CLI 命令的第一个参数是形如 <em>project:target[:configuration]</em> 的目标字符串。</p><p translation-origin="off">The generic <code>ng run</code> CLI command takes as its first argument a target string of the form <em>project:target[:configuration]</em>.</p><ul><li><p translation-result="on"><em>project</em>：与此目标关联的 Angular CLI 项目的名称。</p><p translation-origin="off"><em>project</em>: The name of the Angular CLI project that the target is associated with.</p></li><li><p translation-result="on"><em>target</em>：<code>angular.json</code> 文件 <code>architect</code> 下的指定构建器配置。</p><p translation-origin="off"><em>target</em>: A named builder configuration from the <code>architect</code> section of the <code>angular.json</code> file.</p></li><li><p translation-result="on"><em>configuration</em>：（可选）用于覆盖指定目标的具体配置名称，如 <code>angular.json</code> 文件中的定义。</p><p translation-origin="off"><em>configuration</em>: (optional) The name of a specific configuration override for the given target, as defined in the <code>angular.json</code> file.</p></li></ul><p translation-result="on">如果你的构建器调用另一个构建器，它可能需要读取一个传入的目标字符串。你可以使用 <code>@angular-devkit/architect</code> 中的工具函数 <code>targetFromTargetString()</code> 把这个字符串解析成一个对象。</p><p translation-origin="off">If your builder calls another builder, it may need to read a passed target string. You can parse this string into an object by using the <code>targetFromTargetString()</code> utility function from <code>@angular-devkit/architect</code>.</p><h2 id="schedule-and-run" translation-result="on">调度并运行<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#schedule-and-run"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="schedule-and-run">Schedule and run<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#schedule-and-run"><i class="material-icons">link</i></a></h2><p translation-result="on">建筑师会异步运行构建器。要调用某个构建器，就要在所有配置解析完成之后安排一个要运行的任务。</p><p translation-origin="off">Architect runs builders asynchronously. To invoke a builder, you schedule a task to be run when all configuration resolution is complete.</p><p translation-result="on">在调度器返回 <code>BuilderRun</code> 控件对象之前，不会执行该构建器函数。 CLI 通常会通过调用 <code>BuilderContext.scheduleTarget()</code> 函数来调度任务，然后使用 <code>angular.json</code> 文件中的目标定义来解析输入选项。</p><p translation-origin="off">The builder function is not executed until the scheduler returns a <code>BuilderRun</code> control object. The CLI typically schedules tasks by calling the <code>BuilderContext.scheduleTarget()</code> function, and then resolves input options using the target definition in the <code>angular.json</code> file.</p><p translation-result="on">建筑师会接受默认的选项对象来解析指定目标的输入选项，然后覆盖所用配置中的值（如果有的话），然后再从传给 <code>BuilderContext.scheduleTarget()</code> 的覆盖对象中覆盖这些值。对于 Angular CLI，覆盖对象是从命令行参数中构建的。</p><p translation-origin="off">Architect resolves input options for a given target by taking the default options object, then overwriting values from the configuration used (if any), then further overwriting values from the overrides object passed to <code>BuilderContext.scheduleTarget()</code>. For the Angular CLI, the overrides object is built from command line arguments.</p><p translation-result="on">建筑师会根据构建器的模式对生成的选项值进行验证。如果输入有效，建筑师会创建上下文并执行该构建器。</p><p translation-origin="off">Architect validates the resulting options values against the schema of the builder. If inputs are valid, Architect creates the context and executes the builder.</p><p translation-result="on">欲知详情，请参阅<a href="guide/workspace-config">工作空间配置</a>。</p><p translation-origin="off">For more information see <a href="guide/workspace-config">Workspace Configuration</a>.</p><div class="alert is-helpful"><p translation-result="on">你还可以通过调用 <code>BuilderContext.scheduleBuilder()</code> 从另一个构建器或测试中调用某个构建器。你可以直接把 <code>options</code> 对象传给该方法，并且这些选项值会根据这个构建器的模式进行验证，而无需进一步调整。</p><p translation-origin="off">You can also invoke a builder directly from another builder or test by calling <code>BuilderContext.scheduleBuilder()</code>. You pass an <code>options</code> object directly to the method, and those option values are validated against the schema of the builder without further adjustment.</p><p translation-result="on">只有 <code>BuilderContext.scheduleTarget()</code> 方法来解析这些配置和并通过 <code>angular.json</code> 文件进行覆盖。</p><p translation-origin="off">Only the <code>BuilderContext.scheduleTarget()</code> method resolves the configuration and overrides through the <code>angular.json</code> file.</p></div><h3 id="default-architect-configuration" translation-result="on">默认建筑师配置<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#default-architect-configuration"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="default-architect-configuration">Default architect configuration<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#default-architect-configuration"><i class="material-icons">link</i></a></h3><p translation-result="on">让我们创建一个简单的 <code>angular.json</code> 文件，它会把目标配置放到上下文中。</p><p translation-origin="off">Let’s create a simple <code>angular.json</code> file that puts target configurations into context.</p><p translation-result="on">我们可以把这个构建器发布到 npm（请参阅<a href="https://angular.io/guide/creating-libraries#publishing-your-library">发布你的库</a>），并使用如下命令来安装它：</p><p translation-origin="off">We can publish the builder to npm (see <a href="guide/creating-libraries#publishing-your-library">Publishing your Library</a>), and install it using the following command:</p><code-example language="sh" ng-version="9.0.0-rc.11"><div style="display:none">npm install @example/command-runner</div><aio-code><pre class="lang-sh prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">npm install </span><span class="lit">@example</span><span class="pun">/</span><span class="pln">command</span><span class="pun">-</span><span class="pln">runner</span></code>
    </pre></aio-code></code-example><p translation-result="on">如果我们使用 <code>ng new builder-test</code> 创建一个新项目，那么生成的 <code>angular.json</code> 文件就是这样的，它只有默认的构建器参数。</p><p translation-origin="off">If we create a new project with <code>ng new builder-test</code>, the generated <code>angular.json</code> file looks something like this, with only default builder configurations.</p><code-example language="json" header="angular.json" ng-version="9.0.0-rc.11"><div style="display:none">{ // ... "projects": { // ... "builder-test": { // ... "architect": { // ... "build": { "builder": "@angular-devkit/build-angular:browser", "options": { // ... more options... "outputPath": "dist/builder-test", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "src/tsconfig.app.json" }, "configurations": { "production": { // ... more options... "optimization": true, "aot": true, "buildOptimizer": true } } } } } } // ... }</div><header class="ng-star-inserted">angular.json</header><aio-code class="headed-code"><pre class="lang-json prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from angular.json">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pun">{</span><span class="pln">
  </span><span class="pun">//</span><span class="pln"> </span><span class="pun">...</span><span class="pln">
  </span><span class="str">"projects"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="pun">//</span><span class="pln"> </span><span class="pun">...</span><span class="pln">
    </span><span class="str">"builder-test"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="pun">//</span><span class="pln"> </span><span class="pun">...</span><span class="pln">
      </span><span class="str">"architect"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="pun">//</span><span class="pln"> </span><span class="pun">...</span><span class="pln">
        </span><span class="str">"build"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="str">"builder"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"@angular-devkit/build-angular:browser"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"options"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="pun">//</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> more options</span><span class="pun">...</span><span class="pln">
            </span><span class="str">"outputPath"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"dist/builder-test"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"index"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/index.html"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/main.ts"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"polyfills"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/polyfills.ts"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"tsConfig"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/tsconfig.app.json"</span><span class="pln">
          </span><span class="pun">},</span><span class="pln">
          </span><span class="str">"configurations"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">"production"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              </span><span class="pun">//</span><span class="pln"> </span><span class="pun">...</span><span class="pln"> more options</span><span class="pun">...</span><span class="pln">
              </span><span class="str">"optimization"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
              </span><span class="str">"aot"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
              </span><span class="str">"buildOptimizer"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">//</span><span class="pln"> </span><span class="pun">...</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><h3 id="adding-a-target" translation-result="on">添加一个目标<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#adding-a-target"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="adding-a-target">Adding a target<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#adding-a-target"><i class="material-icons">link</i></a></h3><p translation-result="on">让我们添加一个新的目标来运行我们的构建器执行一个特定的命令。这个目标会告诉构建器在文件上运行 <code>touch</code>，以便更新修改过的日期。</p><p translation-origin="off">Let's add a new target that will run our builder to execute a particular command. This target will tell the builder to run <code>touch</code> on a file, in order to update its modified date.</p><p translation-result="on">我们需要更新 <code>angular.json</code> 文件，把这个构建器的目标添加到新项目的 <code>architect</code> 部分。</p><p translation-origin="off">We need to update the <code>angular.json</code> file to add a target for this builder to the "architect" section of our new project.</p><ul><li><p translation-result="on">我们会为项目的 <code>architect</code> 对象添加一个新的目标小节。</p><p translation-origin="off">We'll add a new target section to the "architect" object for our project.</p></li><li><p translation-result="on">名为 <code>touch</code> 的目标使用了我们的构建器，它发布到了 <code>@example/command-runner</code>。 （参见<a href="https://angular.io/guide/creating-libraries#publishing-your-library">发布你的库</a> ）</p><p translation-origin="off">The target named "touch" uses our builder, which we published to <code>@example/command-runner</code>. (See <a href="guide/creating-libraries#publishing-your-library">Publishing your Library</a>)</p></li><li><p translation-result="on">这个配置对象为我们定义的两个输入提供了默认值：<code>command</code>（要执行的 Unix 命令）和 <code>args</code> （包含要操作的文件的数组）。</p><p translation-origin="off">The options object provides default values for the two inputs that we defined; <code>command</code>, which is the Unix command to execute, and <code>args</code>, an array that contains the file to operate on.</p></li><li><p translation-result="on">这些配置键都是可选的，但我们先不展开。</p><p translation-origin="off">The configurations key is optional, we'll leave it out for now.</p></li></ul><code-example language="json" header="angular.json" ng-version="9.0.0-rc.11"><div style="display:none">{ "projects": { "builder-test": { "architect": { "touch": { "builder": "@example/command-runner:command", "options": { "command": "touch", "args": [ "src/main.ts" ] } }, "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "dist/builder-test", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "src/tsconfig.app.json" }, "configurations": { "production": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "optimization": true, "aot": true, "buildOptimizer": true } } } } } } }</div><header class="ng-star-inserted">angular.json</header><aio-code class="headed-code"><pre class="lang-json prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from angular.json">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pun">{</span><span class="pln">
  </span><span class="str">"projects"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="str">"builder-test"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      </span><span class="str">"architect"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        </span><span class="str">"touch"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="str">"builder"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"@example/command-runner:command"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"options"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">"command"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"touch"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"args"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
              </span><span class="str">"src/main.ts"</span><span class="pln">
            </span><span class="pun">]</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">},</span><span class="pln">
        </span><span class="str">"build"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
          </span><span class="str">"builder"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"@angular-devkit/build-angular:browser"</span><span class="pun">,</span><span class="pln">
          </span><span class="str">"options"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">"outputPath"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"dist/builder-test"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"index"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/index.html"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"main"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/main.ts"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"polyfills"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/polyfills.ts"</span><span class="pun">,</span><span class="pln">
            </span><span class="str">"tsConfig"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/tsconfig.app.json"</span><span class="pln">
          </span><span class="pun">},</span><span class="pln">
          </span><span class="str">"configurations"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
            </span><span class="str">"production"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
              </span><span class="str">"fileReplacements"</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
                </span><span class="pun">{</span><span class="pln">
                  </span><span class="str">"replace"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/environments/environment.ts"</span><span class="pun">,</span><span class="pln">
                  </span><span class="str">"with"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"src/environments/environment.prod.ts"</span><span class="pln">
                </span><span class="pun">}</span><span class="pln">
              </span><span class="pun">],</span><span class="pln">
              </span><span class="str">"optimization"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
              </span><span class="str">"aot"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln">
              </span><span class="str">"buildOptimizer"</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
            </span><span class="pun">}</span><span class="pln">
          </span><span class="pun">}</span><span class="pln">
        </span><span class="pun">}</span><span class="pln">
      </span><span class="pun">}</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
  </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></code>
    </pre></aio-code></code-example><h3 id="running-the-builder" translation-result="on">运行这个构建器<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#running-the-builder"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="running-the-builder">Running the builder<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#running-the-builder"><i class="material-icons">link</i></a></h3><p translation-result="on">要想使用这个新目标的默认配置运行我们的构建器，请在 Linux shell 中使用以下 CLI 命令。</p><p translation-origin="off">To run our builder with the new target's default configuration, use the following CLI command in a Linux shell.</p><code-example language="sh" ng-version="9.0.0-rc.11"><div style="display:none">ng run builder-test:touch</div><aio-code><pre class="lang-sh prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">ng run builder</span><span class="pun">-</span><span class="pln">test</span><span class="pun">:</span><span class="pln">touch</span></code>
    </pre></aio-code></code-example><p translation-result="on">这将在 <code>src/main.ts</code> 文件上运行 <code>touch</code> 命令。</p><p translation-origin="off">This will run the <code>touch</code> command on the <code>src/main.ts</code> file.</p><p translation-result="on">你可以使用命令行参数来覆盖已配置的默认值。例如，要改用其它 <code>command</code> 值运行，请使用以下 CLI 命令。</p><p translation-origin="off">You can use command-line arguments to override the configured defaults. For example, to run with a different <code>command</code> value, use the following CLI command.</p><code-example language="sh" ng-version="9.0.0-rc.11"><div style="display:none">ng run builder-test:touch --command=ls</div><aio-code><pre class="lang-sh prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="pln">ng run builder</span><span class="pun">-</span><span class="pln">test</span><span class="pun">:</span><span class="pln">touch </span><span class="pun">--</span><span class="pln">command</span><span class="pun">=</span><span class="pln">ls</span></code>
    </pre></aio-code></code-example><p translation-result="on">这将调用 <code>ls</code> 命令而不是 <code>touch</code> 命令。因为我们没有覆盖 <em>args</em> 选项，所以它会列出 <code>src/main.ts</code> 文件的信息（提供给该目标的默认值）。</p><p translation-origin="off">This will call the <code>ls</code> command instead of the <code>touch</code> command. Because we did not override the <em>args</em> option, it will list information about the <code>src/main.ts</code> file (the default value provided for the target).</p><h2 id="testing-a-builder" translation-result="on">测试一个构建器<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#testing-a-builder"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="testing-a-builder">Testing a builder<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#testing-a-builder"><i class="material-icons">link</i></a></h2><p translation-result="on">对构建器进行集成测试，以便你可以使用建筑师的调度器来创建一个上下文，就像这个<a href="https://github.com/mgechev/cli-builders-demo">例子</a>中一样。</p><p translation-origin="off">Use integration testing for your builder, so that you can use the Architect scheduler to create a context, as in this <a href="https://github.com/mgechev/cli-builders-demo">example</a>.</p><ul><li><p translation-result="on">在构建器的源码目录下，我们创建了一个新的测试文件 <code>index.spec.ts</code>。该代码创建了 <code>JsonSchemaRegistry</code>（用于模式验证）、<code>TestingArchitectHost</code>（对 <code>ArchitectHost</code> 的内存实现）和 <code>Architect</code> 的新实例。</p><p translation-origin="off">In the builder source directory, we have created a new test file <code>my-builder.spec.ts</code>. The code creates new instances of <code>JsonSchemaRegistry</code> (for schema validation), <code>TestingArchitectHost</code> (an in-memory implementation of <code>ArchitectHost</code>), and <code>Architect</code>.</p></li><li><p translation-result="on">我们紧挨着这个构建器的 <a href="https://github.com/mgechev/cli-builders-demo/blob/master/command-builder/builders.json"><code>package.json</code></a>文件添加了一个 <code>builders.json</code> 文件，并修改了 <code>package.json</code> 文件以指向它。</p><p translation-origin="off">We've added a <code>builders.json</code> file next to the builder's <a href="https://github.com/mgechev/cli-builders-demo/blob/master/command-builder/builders.json"><code>package.json</code> file</a>, and modified the package file to point to it.</p></li></ul><p translation-result="on">下面是运行该命令构建器的测试范例。该测试使用该构建器来运行 <code>node --print 'foo'</code> 命令，然后验证 <code>logger</code> 中是否包含一条 <code>foo</code> 记录。</p><p translation-origin="off">Here’s an example of a test that runs the command builder. The test uses the builder to run the <code>node --print 'foo'</code> command, then validates that the <code>logger</code> contains an entry for <code>foo</code>.</p><code-example path="cli-builder/src/my-builder.spec.ts" header="src/my-builder.spec.ts" ng-version="9.0.0-rc.11"><div style="display:none">import { Architect } from '@angular-devkit/architect'; import { TestingArchitectHost } from '@angular-devkit/architect/testing'; import { logging, schema } from '@angular-devkit/core'; describe('Command Runner Builder', () =&gt; { let architect: Architect; let architectHost: TestingArchitectHost; beforeEach(<a href="api/core/testing/async" class="code-anchor">async</a> () =&gt; { const registry = new schema.CoreSchemaRegistry(); registry.addPostTransform(schema.transforms.addUndefinedDefaults); // TestingArchitectHost() takes workspace and current directories. // Since we don't use those, both are the same in this case. architectHost = new TestingArchitectHost(__dirname, __dirname); architect = new Architect(architectHost, registry); // This will either take a Node package name, or a path to the directory // for the package.json file. await architectHost.addBuilderFromPackage('..'); }); it('can run node', <a href="api/core/testing/async" class="code-anchor">async</a> () =&gt; { // Create a logger that keeps an array of all <a href="api/service-worker/SwPush#messages" class="code-anchor">messages</a> that were logged. const logger = new logging.Logger(''); const logs = []; logger.subscribe(ev =&gt; logs.push(ev.message)); // A "run" can have <a href="api/forms/SelectMultipleControlValueAccessor" class="code-anchor">multiple</a> outputs, and contains progress information. const run = await architect.scheduleBuilder('@example/command-runner:command', { command: 'node', args: ['--print', '\'foo\''], }, { logger }); // We pass the logger for checking later. // The "result" member (of type BuilderOutput) is the next output. const output = await run.result; // Stop the builder from running. This stops Architect from keeping // the builder-associated states in memory, since builders keep waiting // to be scheduled. await run.stop(); // Expect that foo was logged expect(logs).toContain('foo'); }); });</div><header class="ng-star-inserted">src/my-builder.spec.ts</header><aio-code class="headed-code"><pre class="lang- prettyprint">      <button title="Copy code snippet" class="material-icons copy-button no-print ng-star-inserted" aria-label="Copy code snippet from src/my-builder.spec.ts">
        <span aria-hidden="true">content_copy</span>
      </button>
      <code class="animated fadeIn"><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">Architect</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/architect'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="typ">TestingArchitectHost</span><span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/architect/testing'</span><span class="pun">;</span><span class="pln">
</span><span class="kwd">import</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> logging</span><span class="pun">,</span><span class="pln"> schema </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">from</span><span class="pln"> </span><span class="str">'@angular-devkit/core'</span><span class="pun">;</span><span class="pln">

describe</span><span class="pun">(</span><span class="str">'Command Runner Builder'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
  </span><span class="kwd">let</span><span class="pln"> architect</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Architect</span><span class="pun">;</span><span class="pln">
  </span><span class="kwd">let</span><span class="pln"> architectHost</span><span class="pun">:</span><span class="pln"> </span><span class="typ">TestingArchitectHost</span><span class="pun">;</span><span class="pln">

  beforeEach</span><span class="pun">(</span><a href="api/core/testing/async" class="code-anchor"><span class="kwd">async</span></a><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> registry </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> schema</span><span class="pun">.</span><span class="typ">CoreSchemaRegistry</span><span class="pun">();</span><span class="pln">
    registry</span><span class="pun">.</span><span class="pln">addPostTransform</span><span class="pun">(</span><span class="pln">schema</span><span class="pun">.</span><span class="pln">transforms</span><span class="pun">.</span><span class="pln">addUndefinedDefaults</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// TestingArchitectHost() takes workspace and current directories.</span><span class="pln">
    </span><span class="com">// Since we don't use those, both are the same in this case.</span><span class="pln">
    architectHost </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">TestingArchitectHost</span><span class="pun">(</span><span class="pln">__dirname</span><span class="pun">,</span><span class="pln"> __dirname</span><span class="pun">);</span><span class="pln">
    architect </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Architect</span><span class="pun">(</span><span class="pln">architectHost</span><span class="pun">,</span><span class="pln"> registry</span><span class="pun">);</span><span class="pln">

    </span><span class="com">// This will either take a Node package name, or a path to the directory</span><span class="pln">
    </span><span class="com">// for the package.json file.</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> architectHost</span><span class="pun">.</span><span class="pln">addBuilderFromPackage</span><span class="pun">(</span><span class="str">'..'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">

  it</span><span class="pun">(</span><span class="str">'can run node'</span><span class="pun">,</span><span class="pln"> </span><a href="api/core/testing/async" class="code-anchor"><span class="kwd">async</span></a><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="com">// Create a logger that keeps an array of all </span><a href="api/service-worker/SwPush#messages" class="code-anchor"><span class="com">messages</span></a><span class="com"> that were logged.</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> logger </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> logging</span><span class="pun">.</span><span class="typ">Logger</span><span class="pun">(</span><span class="str">''</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> logs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln">
    logger</span><span class="pun">.</span><span class="pln">subscribe</span><span class="pun">(</span><span class="pln">ev </span><span class="pun">=&gt;</span><span class="pln"> logs</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">ev</span><span class="pun">.</span><span class="pln">message</span><span class="pun">));</span><span class="pln">

    </span><span class="com">// A "run" can have </span><a href="api/forms/SelectMultipleControlValueAccessor" class="code-anchor"><span class="com">multiple</span></a><span class="com"> outputs, and contains progress information.</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> run </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> architect</span><span class="pun">.</span><span class="pln">scheduleBuilder</span><span class="pun">(</span><span class="str">'@example/command-runner:command'</span><span class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
      command</span><span class="pun">:</span><span class="pln"> </span><span class="str">'node'</span><span class="pun">,</span><span class="pln">
      args</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'--print'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'\'foo\''</span><span class="pun">],</span><span class="pln">
    </span><span class="pun">},</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> logger </span><span class="pun">});</span><span class="pln">  </span><span class="com">// We pass the logger for checking later.</span><span class="pln">

    </span><span class="com">// The "result" member (of type BuilderOutput) is the next output.</span><span class="pln">
    </span><span class="kwd">const</span><span class="pln"> output </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> run</span><span class="pun">.</span><span class="pln">result</span><span class="pun">;</span><span class="pln">

    </span><span class="com">// Stop the builder from running. This stops Architect from keeping</span><span class="pln">
    </span><span class="com">// the builder-associated states in memory, since builders keep waiting</span><span class="pln">
    </span><span class="com">// to be scheduled.</span><span class="pln">
    </span><span class="kwd">await</span><span class="pln"> run</span><span class="pun">.</span><span class="pln">stop</span><span class="pun">();</span><span class="pln">

    </span><span class="com">// Expect that foo was logged</span><span class="pln">
    expect</span><span class="pun">(</span><span class="pln">logs</span><span class="pun">).</span><span class="pln">toContain</span><span class="pun">(</span><span class="str">'foo'</span><span class="pun">);</span><span class="pln">
  </span><span class="pun">});</span><span class="pln">
</span><span class="pun">});</span></code>
    </pre></aio-code></code-example><div class="alert is-helpful"><p translation-result="on">在你的仓库中运行这个测试时，需要使用 <a href="https://github.com/TypeStrong/ts-node"><code>ts-node</code></a>包。你可以把 <code>index.spec.ts</code> 重命名为 <code>index.spec.js</code> 来回避它。</p><p translation-origin="off">When running this test in your repo, you need the <a href="https://github.com/TypeStrong/ts-node"><code>ts-node</code></a>package. You can avoid this by renaming <code>my-builder.spec.ts</code> to <code>my-builder.spec.js</code>.</p></div><h3 id="watch-mode" translation-result="on">监视（watch）模式<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#watch-mode"><i class="material-icons">link</i></a></h3><h3 translation-origin="off" id="watch-mode">Watch mode<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#watch-mode"><i class="material-icons">link</i></a></h3><p translation-result="on">建筑师希望构建器运行一次（默认情况下）并返回。这种行为与那些需要监视文件更改的构建器（例如 Webpack）并不完全兼容。 建筑师可以支持监视模式，但要注意一些问题。</p><p translation-origin="off">Architect expects builders to run once (by default) and return. This behavior is not entirely compatible with a builder that watches for changes (like Webpack, for example). Architect can support watch mode, but there are some things to look out for.</p><ul><li><p translation-result="on">要在监视模式下使用，构建器处理函数应返回一个 Observable。建筑师会订阅这个 Observable，直到这个 Observable 完成（complete）为止。此外，如果使用相同的参数再次调度这个构建器，建筑师还能重用这个 Observable。</p><p translation-origin="off">To be used with watch mode, a builder handler function should return an Observable. Architect subscribes to the Observable until it completes and might reuse it if the builder is scheduled again with the same arguments.</p></li><li><p translation-result="on">这个构建器应该总是在每次执行后发出一个 <code>BuilderOutput</code> 对象。一旦它被执行，就会进入一个由外部事件触发的监视模式。如果一个事件导致它重启，那么此构建器应该执行 <code>BuilderContext.reportRunning()</code> 函数来告诉建筑师再次运行它。如果调度器还计划了另一次运行，就会阻止建筑师停掉这个构建器。</p><p translation-origin="off">The builder should always emit a <code>BuilderOutput</code> object after each execution. Once it’s been executed, it can enter a watch mode, to be triggered by an external event. If an event triggers it to restart, the builder should execute the <code>BuilderContext.reportRunning()</code> function to tell Architect that it is running again. This prevents Architect from stopping the builder if another run is scheduled.</p></li></ul><p translation-result="on">当你的构建器通过调用 <code>BuilderRun.stop()</code> 来退出监视模式时，建筑师会从构建器的 Observable 中取消订阅，并调用构建器的退出逻辑进行清理。（这种行为也允许停止和清理运行时间过长的构建。）</p><p translation-origin="off">When your builder calls <code>BuilderRun.stop()</code> to exit watch mode, Architect unsubscribes from the builder’s Observable and calls the builder’s teardown logic to clean up. (This behavior also allows for long running builds to be stopped and cleaned up.)</p><p translation-result="on">一般来说，如果你的构建器正在监视一个外部事件，你应该把你的运行分成三个阶段。</p><p translation-origin="off">In general, if your builder is watching an external event, you should separate your run into three phases.</p><ol><li><p translation-result="on"><strong>运行</strong>，例如 webpack 编译。这会在 webpack 完成并且你的构建器发出 <code>BuilderOutput</code> 对象时结束。</p><p translation-origin="off"><strong>Running</strong> For example, webpack compiles. This ends when webpack finishes and your builder emits a <code>BuilderOutput</code> object.</p></li><li><p translation-result="on"><strong>监视</strong>，在两次运行之间监视外部事件流。例如，webpack 会监视文件系统是否发生了任何变化。这会在 webpack 重启构建时结束，并调用 <code>BuilderContext.reportRunning()</code>。这样就会再回到第 1 步。</p><p translation-origin="off"><strong>Watching</strong> Between two runs, watch an external event stream. For example, webpack watches the file system for any changes. This ends when webpack restarts building, and <code>BuilderContext.reportRunning()</code> is called. This goes back to step 1.</p></li><li><p translation-result="on"><strong>完成</strong>，任务完全完成（例如，webpack 应运行多次），或者构建器停止运行（使用 <code>BuilderRun.stop()</code>）。你的退出逻辑被调用了，建筑师也从你的构建器的 Observable 中取消了订阅。</p><p translation-origin="off"><strong>Completion</strong> Either the task is fully completed (for example, webpack was supposed to run a number of times), or the builder run was stopped (using <code>BuilderRun.stop()</code>). Your teardown logic is executed, and Architect unsubscribes from your builder’s Observable.</p></li></ol><h2 id="summary" translation-result="on">总结<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#summary"><i class="material-icons">link</i></a></h2><h2 translation-origin="off" id="summary">Summary<a title="Link to this heading" class="header-link" aria-hidden="true" href="guide/cli-builder#summary"><i class="material-icons">link</i></a></h2><p translation-result="on">CLI 构建器 API 提供了一种通过构建器执行自定义逻辑，以改变 Angular CLI 行为的新方式。</p><p translation-origin="off">The CLI Builder API provides a new way of changing the behavior of the Angular CLI by using builders to execute custom logic.</p><ul><li><p translation-result="on">构建器既可以是同步的，也可以是异步的，它可以只执行一次也可以监视外部事件，还可以调度其它构建器或目标。</p><p translation-origin="off">Builders can be synchronous or asynchronous, execute once or watch for external events, and can schedule other builders or targets.</p></li><li><p translation-result="on">构建器在 <code>angular.json</code> 配置文件中指定了选项的默认值，它可以被目标的备用配置覆盖，还可以进一步被命令行标志所覆盖。</p><p translation-origin="off">Builders have option defaults specified in the <code>angular.json</code> configuration file, which can be overwritten by an alternate configuration for the target, and further overwritten by command line flags.</p></li><li><p translation-result="on">我们建议你使用集成测试来测试建筑师的构建器。你还可以使用单元测试来验证这个构建器的执行逻辑。</p><p translation-origin="off">We recommend that you use integration tests to test Architect builders. You can use unit tests to validate the logic that the builder executes.</p></li><li><p translation-result="on">如果你的构建器返回一个 Observable，你应该在那个 Observable 的退出逻辑中进行清理。</p><p translation-origin="off">If your builder returns an Observable, it should clean up in the teardown logic of that Observable.</p></li></ul></div></div></aio-doc-viewer></main></mat-sidenav-content></mat-sidenav-container><div class="toc-container no-print ng-star-inserted"><aio-lazy-ce selector="aio-toc"><aio-toc ng-version="9.0.0-rc.11"><div class="toc-inner no-print collapsed ng-star-inserted"><ul class="toc-list"><li class="h1 ng-star-inserted active" title="Angular CLI 构建器（Builder）"><a href="guide/cli-builder#angular-cli-builders">Angular CLI 构建器（Builder）</a></li><li class="h2 ng-star-inserted" title="CLI 构建器"><a href="guide/cli-builder#cli-builders">CLI 构建器</a></li><li class="h3 ng-star-inserted" title="构建器的项目结构"><a href="guide/cli-builder#builder-project-structure">构建器的项目结构</a></li><li class="h2 ng-star-inserted" title="创建构建器"><a href="guide/cli-builder#creating-a-builder">创建构建器</a></li><li class="h3 ng-star-inserted" title="处理输出"><a href="guide/cli-builder#handling-output">处理输出</a></li><li class="h3 ng-star-inserted" title="进度和状态报告"><a href="guide/cli-builder#progress-and-status-reporting">进度和状态报告</a></li><li class="h2 ng-star-inserted" title="构建器的输入"><a href="guide/cli-builder#builder-input">构建器的输入</a></li><li class="h3 ng-star-inserted" title="对输入的验证"><a href="guide/cli-builder#input-validation">对输入的验证</a></li><li class="h3 ng-star-inserted" title="目标配置"><a href="guide/cli-builder#target-configuration">目标配置</a></li><li class="h2 ng-star-inserted" title="调度并运行"><a href="guide/cli-builder#schedule-and-run">调度并运行</a></li><li class="h3 ng-star-inserted" title="默认建筑师配置"><a href="guide/cli-builder#default-architect-configuration">默认建筑师配置</a></li><li class="h3 ng-star-inserted" title="添加一个目标"><a href="guide/cli-builder#adding-a-target">添加一个目标</a></li><li class="h3 ng-star-inserted" title="运行这个构建器"><a href="guide/cli-builder#running-the-builder">运行这个构建器</a></li><li class="h2 ng-star-inserted" title="测试一个构建器"><a href="guide/cli-builder#testing-a-builder">测试一个构建器</a></li><li class="h3 ng-star-inserted" title="监视（watch）模式"><a href="guide/cli-builder#watch-mode">监视（watch）模式</a></li><li class="h2 ng-star-inserted" title="总结"><a href="guide/cli-builder#summary">总结</a></li></ul></div></aio-toc></aio-lazy-ce></div><footer class="no-print"><aio-footer><div class="grid-fluid"><div class="footer-block ng-star-inserted"><h3>资源</h3><ul><li class="ng-star-inserted"><a class="link" href="about" title="Angular 贡献者。">关于</a></li><li class="ng-star-inserted"><a class="link" href="resources" title="网络上的 Angular 工具、培训、博客等">资源列表</a></li><li class="ng-star-inserted"><a class="link" href="presskit" title="我们的联系方式、LOGO 和品牌">宣传资料</a></li><li class="ng-star-inserted"><a class="link" href="https://blog.angular.io/" title="Angular 官方博客">博客</a></li><li class="ng-star-inserted"><a class="link" href="analytics" title="Angular 使用情况分析">使用情况分析</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>帮助</h3><ul><li class="ng-star-inserted"><a class="link" href="https://stackoverflow.com/questions/tagged/angular" title="Stack Overflow: 这里的社区会回答你关于 Angular 的技术问题">Stack Overflow</a></li><li class="ng-star-inserted"><a class="link" href="https://gitter.im/angular/angular" title="和老鸟聊 Angular">Gitter</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/angular/issues" title="在 github 上报告问题和建议。">报告问题</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md" title="让我们彼此尊重">行为规范</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>社区</h3><ul><li class="ng-star-inserted"><a class="link" href="events" title="Angular events around the world.">活动</a></li><li class="ng-star-inserted"><a class="link" href="http://www.meetup.com/topics/angularjs/" title="参加聚会，向别的开发人员学习">聚会</a></li><li class="ng-star-inserted"><a class="link" href="https://twitter.com/angular" title="Twitter">Twitter</a></li><li class="ng-star-inserted"><a class="link" href="https://github.com/angular/angular" title="GitHub">GitHub</a></li><li class="ng-star-inserted"><a class="link" href="contribute" title="向 Angular 做贡献">做贡献</a></li></ul></div><div class="footer-block ng-star-inserted"><h3>多语言</h3><ul><li class="ng-star-inserted"><a class="link" href="https://angular.io/" title="English Version.">English Version</a></li><li class="ng-star-inserted"><a class="link" href="https://angular.tw/" title="正體中文版">正體中文版</a></li><li class="ng-star-inserted"><a class="link" href="https://angular.jp/" title="日本語版">日本語版</a></li><li class="ng-star-inserted"><a class="link" href="https://angular.kr/" title="한국어">한국어</a></li></ul></div></div><p>Super-powered by Google ©2010-2020. 代码授权方式：<a href="license" title="License text">MIT-style License</a>. 文档授权方式：<a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>.</p><p>当前版本：9.0.1-build.79+sha.55f1ef01.</p></aio-footer></footer><div class="cdk-visually-hidden ng-star-inserted"><mat-icon role="img" class="mat-icon notranslate material-icons mat-icon-no-color" aria-hidden="true">&nbsp;</mat-icon></div></aio-shell><noscript><div class="background-sky hero"></div><section id="intro" style="text-shadow:1px 1px #1976d2"><div class="hero-logo"><img src="assets/images/logos/angular/angular.svg" width="250" height="250" alt="Angular"></div><div class="homepage-container"><div class="hero-headline">一套框架，多种平台<br>移动 &amp; 桌面</div></div></section><h2 style="color:red;margin-top:40px;position:relative;text-align:center;text-shadow:1px 1px #fafafa"><b><i>该网站需要浏览器支持 JavaScript</i></b></h2></noscript><script src="runtime-es2015.ee607cf78010348c8334.js" type="module"></script><script src="runtime-es5.ee607cf78010348c8334.js" nomodule="" defer=""></script><script src="polyfills-es5.1494ca908613f3429457.js" nomodule="" defer=""></script><script src="polyfills-es2015.9057a05c650bf60ecbf4.js" type="module"></script><script src="main-es2015.9cd62e243bd182514a19.js" type="module"></script><script src="main-es5.9cd62e243bd182514a19.js" nomodule="" defer=""></script><div class="cdk-live-announcer-element cdk-visually-hidden" aria-atomic="true" aria-live="polite"></div></body></html>